Thông số kỹ thuật của StableHLO

StableHLO là một tập hợp các thao tác cho các thao tác cấp cao (HLO) trong các mô hình học máy (ML). StableHLO hoạt động như một lớp di động giữa các trình biên dịch ML và khung ML khác nhau: Các khung ML tạo ra các chương trình StableHLO tương thích với các trình biên dịch ML sử dụng các chương trình StableHLO.

Mục tiêu của chúng tôi là đơn giản hoá và đẩy nhanh quá trình phát triển ML bằng cách tạo khả năng tương tác cao hơn giữa nhiều khung ML (chẳng hạn như TensorFlow, JAX và PyTorch) và trình biên dịch ML (chẳng hạn như XLA và IREE). Để đạt được mục tiêu đó, tài liệu này cung cấp một quy cách cho ngôn ngữ lập trình StableHLO.

Quy cách này có 3 phần chính. Trước tiên, phần Chương trình mô tả cấu trúc của các chương trình StableHLO bao gồm các hàm StableHLO, bản thân các hàm này bao gồm các thao tác StableHLO. Trong cấu trúc đó, phần Ops chỉ định ngữ nghĩa của từng thao tác. Phần Thực thi cung cấp ngữ nghĩa cho tất cả các thao tác này cùng thực thi trong một chương trình. Cuối cùng, phần Ký hiệu sẽ thảo luận về ký hiệu được dùng trong toàn bộ quy cách.

Để xem quy cách của một bản phát hành trước của StableHLO, hãy mở kho lưu trữ tại bản phát hành được gắn thẻ mà bạn quan tâm. Ví dụ: Quy cách StableHLO phiên bản 0.19.0. Để xem những thay đổi xảy ra ở mỗi lần tăng phiên bản phụ của StableHLO, hãy tham khảo nhật ký phiên bản trong VhloDialect.td.

Chương trình

Program ::= {Func}

Các chương trình StableHLO bao gồm một số lượng tuỳ ý các hàm StableHLO. Dưới đây là một chương trình mẫu có hàm @main với 3 đầu vào (%image, %weights%bias) và 1 đầu ra. Nội dung của hàm có 6 thao tác.

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>) -> ()
}

Hàm

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

Các hàm StableHLO (còn được gọi là các hàm được đặt tên) có một giá trị nhận dạng, dữ liệu đầu vào/đầu ra và một phần nội dung. Trong tương lai, chúng tôi dự định giới thiệu thêm siêu dữ liệu cho các hàm để đạt được khả năng tương thích tốt hơn với HLO (#425, #626, #740, #744).

Giá trị nhận dạng

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

Giá trị nhận dạng StableHLO tương tự như giá trị nhận dạng trong nhiều ngôn ngữ lập trình, với 2 đặc điểm riêng biệt: 1) tất cả giá trị nhận dạng đều có ký hiệu để phân biệt các loại giá trị nhận dạng, 2) giá trị nhận dạng có thể hoàn toàn là số để đơn giản hoá việc tạo chương trình StableHLO.

Loại

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

Các loại StableHLO được phân loại thành các loại giá trị (còn được gọi là các loại hạng nhất) đại diện cho các giá trị StableHLO và các loại không phải giá trị mô tả các phần tử khác của chương trình. Các loại StableHLO tương tự như các loại trong nhiều ngôn ngữ lập trình, điểm đặc biệt chính là bản chất dành riêng cho miền của StableHLO, dẫn đến một số kết quả bất thường (ví dụ: các loại vô hướng không phải là các loại giá trị).

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

Các loại tensor biểu thị tensor, tức là mảng đa chiều. Chúng có một hình dạng và một loại phần tử, trong đó hình dạng biểu thị kích thước chiều không âm hoặc không xác định theo thứ tự tăng dần của các chiều tương ứng (còn được gọi là trục) được đánh số từ 0 đến R-1. Số lượng phương diện R được gọi là hạng. Ví dụ: tensor<2x3xf32> là một loại tensor có hình dạng 2x3 và loại phần tử f32. Mảng này có 2 phương diện (hay nói cách khác là 2 trục) – phương diện thứ 0 và phương diện thứ 1 – có kích thước là 2 và 3. Thứ hạng của nó là 2.

Các hình dạng có thể không xác định được một phần hoặc hoàn toàn (động), ví dụ: tensor<?x2xf64> không xác định được một phần và tensor<?x?xf64> không xác định được hoàn toàn. Kích thước phương diện động được biểu thị bằng ?. Không thể huỷ xếp hạng hình dạng.

Trong tương lai, chúng tôi dự định khám phá việc mở rộng các loại tensor ngoài kích thước và loại phần tử, ví dụ: bao gồm bố cục (#629) và độ thưa thớt (#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
Tên Loại Giới hạn
storage_type loại số nguyên (C1-C3), (C8)
storage_min hằng số nguyên (C1), (C3), (C7)
storage_max hằng số nguyên (C2), (C3), (C7)
expressed_type loại dấu phẩy động (C4)
quantization_dimension hằng số nguyên không bắt buộc (C10-C12)
scales số lượng hằng số có thể thay đổi của số thực (C4-C6), (C9), (C10), (C13)
zero_points số lượng hằng số nguyên có thể thay đổi (C7 – C9)

Các loại phần tử được lượng tử hoá biểu thị các giá trị nguyên của một loại lưu trữ trong phạm vi từ storage_min đến storage_max (bao gồm) tương ứng với các giá trị dấu phẩy động của một loại được biểu thị. Đối với một giá trị số nguyên i nhất định, giá trị dấu phẩy động f tương ứng có thể được tính là f = (i - zero_point) * scale, trong đó scalezero_point được gọi là các tham số định lượng. storage_minstorage_max là các giá trị không bắt buộc trong ngữ pháp, nhưng có các giá trị mặc định là min_value(storage_type)max_value(storage_type) tương ứng. Các loại phần tử được lượng tử hoá có những ràng buộc sau:

  • (C1) type(storage_min) = storage_type.
  • (C2) type(storage_max) = storage_type.
  • (C3) min_value(storage_type) <= storage_min < storage_max <= max_value(storage_type).
  • (C4) type(scales...) = expressed_type.
  • (C5) 0 < scales.
  • (C6) is_finite(scales...).
  • (C7) storage_min <= zero_points <= storage_max.
  • (C8) type(zero_points...) = storage_type.
  • (C9) size(scales) = size(zero_points).
  • (C10) Nếu is_empty(quantization_dimension), thì size(scales) = 1.
  • (C11) 0 <= quantization_dimension.

Hiện tại, QuantizationScale là một hằng số dấu phẩy động, nhưng có nhiều người quan tâm đến các tỷ lệ dựa trên số nguyên, được biểu thị bằng hệ số nhân và các giá trị dịch chuyển. Chúng tôi dự định sẽ khám phá vấn đề này trong tương lai gần (#1404).

Hiện đang có một cuộc thảo luận về ngữ nghĩa của QuantizationZeroPoint, bao gồm loại, giá trị và liệu có thể chỉ có một hoặc có thể có nhiều điểm 0 trong một loại tensor được lượng tử hoá hay không. Dựa trên kết quả của cuộc thảo luận này, quy cách về điểm bằng 0 có thể thay đổi trong tương lai (#1405).

Một cuộc thảo luận khác đang diễn ra liên quan đến ngữ nghĩa của QuantizationStorageMinQuantizationStorageMax để xác định xem có nên áp đặt bất kỳ hạn chế nào đối với các giá trị này và đối với các giá trị của tensor được lượng tử hoá hay không (#1406).

Cuối cùng, chúng tôi dự định khám phá cách biểu thị các tỷ lệ không xác định và điểm không, tương tự như cách chúng tôi dự định khám phá cách biểu thị các kích thước phương diện không xác định (#1407).

Các loại tensor lượng tử hoá biểu thị các tensor có phần tử lượng tử hoá. Các tensor này hoàn toàn giống với tensor thông thường, ngoại trừ việc các phần tử của chúng có các loại phần tử được lượng tử hoá, thay vì các loại phần tử thông thường.

Trong các tensor được lượng tử hoá, quá trình lượng tử hoá có thể là trên mỗi tensor, tức là có một scalezero_point cho toàn bộ tensor hoặc có thể là trên mỗi trục, tức là có nhiều scaleszero_points, một cặp cho mỗi lát cắt của một phương diện cụ thể quantization_dimension. Cụ thể hơn, trong một tensor t có lượng tử hoá trên mỗi trục, sẽ có dim(t, quantization_dimension) lát của quantization_dimension: t[:, ..., 0, ..., :], t[:, ..., 1, ..., :], v.v. Tất cả các phần tử trong lát thứ i đều dùng scales[i]zero_points[i] làm tham số lượng tử hoá. Các loại tensor được lượng tử hoá có những ràng buộc sau:

  • Đối với lượng tử hoá trên mỗi tensor:
    • Không có thêm ràng buộc nào khác.
  • Đối với lượng tử hoá trên mỗi trục:
    • (C12) quantization_dimension < rank(self).
    • (C13) dim(self, quantization_dimension) = size(scales).
TokenType ::= 'token'

Loại mã thông báo biểu thị mã thông báo, tức là các giá trị không công khai do một số thao tác tạo và sử dụng. Mã thông báo được dùng để áp đặt thứ tự thực thi cho các thao tác như mô tả trong phần Thực thi.

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

Các loại bộ đệm đại diện cho bộ đệm. Ví dụ: trong XLA, các vùng đệm là mảng đa chiều có bộ nhớ nhất quán. Tương tự như các loại tensor, các loại vùng đệm có một hình dạng và một loại phần tử, trong đó hình dạng biểu thị kích thước chiều không âm hoặc không xác định theo thứ tự tăng dần của các chiều tương ứng (còn được gọi là trục) được đánh số từ 0 đến R-1. Số lượng phương diện R được gọi là hạng. Ví dụ: memref<2x3xf32> là một loại vùng đệm có hình dạng 2x3 và loại phần tử f32. Nó có 2 phương diện (hay nói cách khác là 2 trục) – phương diện thứ 0 và phương diện thứ 1 – có kích thước là 2 và 3. Thứ hạng của nó là 2.

Bạn có thể phân bổ các vùng đệm bằng cách sử dụng custom_call thành CreateBuffer hoặc Pin và huỷ phân bổ thông qua custom_call thành Unpin. Chỉ các thao tác custom_call mới có thể đọc và ghi nội dung bên trong các vùng đệm. Hãy xem custom_call để biết thêm thông tin chi tiết.

Các loại bộ biểu thị các bộ, tức là các danh sách không đồng nhất. Bộ giá trị là một tính năng cũ chỉ tồn tại để tương thích với HLO. Trong HLO, các bộ được dùng để biểu thị đầu vào và đầu ra có thể thay đổi. Trong StableHLO, các đầu vào và đầu ra có số lượng thay đổi được hỗ trợ một cách tự nhiên và việc sử dụng bộ dữ liệu duy nhất trong StableHLO là để thể hiện toàn diện ABI HLO, trong đó chẳng hạn như T, tuple<T>tuple<tuple<T>> có thể khác biệt đáng kể tuỳ thuộc vào một cách triển khai cụ thể. Trong tương lai, chúng tôi dự định thay đổi ABI HLO. Điều này có thể cho phép chúng tôi xoá các loại bộ dữ liệu khỏi 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'

Các loại phần tử đại diện cho các phần tử của các loại tensor. Không giống như trong nhiều ngôn ngữ lập trình, các loại này không phải là loại hạng nhất trong StableHLO. Điều này có nghĩa là các chương trình StableHLO không thể trực tiếp biểu thị các giá trị thuộc những loại này (do đó, theo thông lệ, bạn nên biểu thị các giá trị vô hướng thuộc loại T bằng các giá trị tensor 0 chiều thuộc loại tensor<T>).

  • Loại boolean biểu thị các giá trị boolean truefalse.
  • Các loại số nguyên có thể là số nguyên có dấu (si) hoặc số nguyên không dấu (ui) và có một trong các độ rộng bit được hỗ trợ (2, 4, 8, 16, 32 hoặc 64). Các loại siN có dấu biểu thị các giá trị số nguyên từ -2^(N-1) đến 2^(N-1)-1 (bao gồm cả 2 giá trị này), còn các loại uiN không dấu biểu thị các giá trị số nguyên từ 0 đến 2^N-1 (bao gồm cả 2 giá trị này).
  • Các loại số thực có thể là một trong những loại sau:
  • Các kiểu phức tạp biểu thị các giá trị phức tạp có phần thựcphần ảo của cùng một kiểu phần tử. Các loại phức tạp được hỗ trợ là complex<f32> (cả hai phần đều thuộc loại f32) và complex<f64> (cả hai phần đều thuộc loại f64).
FunctionType ::= '(' InputTypes ')' '->' '(' OutputTypes ')'
InputTypes ::= [ValueType {',' ValueType}]
OutputTypes ::= [ValueType {',' ValueType}]

Các loại hàm biểu thị cả hàm được đặt tên và hàm ẩn danh. Chúng có các loại đầu vào (danh sách các loại ở phía bên trái của ->) và các loại đầu ra (danh sách các loại ở phía bên phải của ->). Trong nhiều ngôn ngữ lập trình, các loại hàm là loại đầu tiên, nhưng không phải trong StableHLO.

StringType ::= 'string'

Loại chuỗi biểu thị các chuỗi byte. Không giống như trong nhiều ngôn ngữ lập trình, loại chuỗi không phải là loại dữ liệu hạng nhất trong StableHLO và chỉ được dùng để chỉ định siêu dữ liệu tĩnh cho các phần tử chương trình.

Hoạt động tính toán

Các thao tác StableHLO (còn được gọi là ops) đại diện cho một tập hợp khép kín gồm các thao tác cấp cao trong các mô hình học máy. Như đã thảo luận ở trên, cú pháp StableHLO chịu ảnh hưởng lớn của MLIR. Đây không nhất thiết là lựa chọn thay thế tiện dụng nhất, nhưng có thể nói là phù hợp nhất với mục tiêu của StableHLO là tạo ra khả năng tương tác cao hơn giữa các khung ML và trình biên dịch ML.

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

Các thao tác StableHLO (còn được gọi là ops) có tên, dữ liệu đầu vào/đầu ra và chữ ký. Tên này bao gồm tiền tố stablehlo. và một từ viết tắt giúp xác định duy nhất một trong các thao tác được hỗ trợ. Xem bên dưới để biết danh sách đầy đủ tất cả các thao tác được hỗ trợ.

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

Các thao tác sử dụng đầu vào và tạo ra đầu ra. Đầu vào được phân loại thành các giá trị đầu vào (được tính toán trong quá trình thực thi), hàm đầu vào (được cung cấp tĩnh, vì trong StableHLO, các hàm không phải là giá trị lớp thứ nhất) và các thuộc tính đầu vào (cũng được cung cấp tĩnh). Loại đầu vào và đầu ra mà một thao tác sử dụng và tạo ra phụ thuộc vào từ viết tắt của thao tác đó. Ví dụ: thao tác add sử dụng 2 giá trị đầu vào và tạo ra 1 giá trị đầu ra. Để so sánh, thao tác select_and_scatter sẽ sử dụng 3 giá trị đầu vào, 2 hàm đầu vào và 3 thuộc tính đầu vào.

OpInputFunc ::= '{' Unused FuncInputs ':' FuncBody '}'
Unused      ::= '^' digit {digit}
              | '^' letter {letter | digit}

Hàm đầu vào (còn được gọi là hàm ẩn danh) rất giống với hàm được đặt tên, ngoại trừ: 1) chúng không có giá trị nhận dạng (do đó có tên là "ẩn danh"), 2) chúng không khai báo các loại đầu ra (các loại đầu ra được suy luận từ thao tác return trong hàm).

Cú pháp cho các hàm đầu vào bao gồm một phần hiện không dùng đến (xem phần Unused ở trên) để tương thích với MLIR. Trong MLIR, có một khái niệm chung hơn về "vùng" có thể có nhiều "khối" của các thao tác được kết nối với nhau thông qua các thao tác nhảy. Các khối này có mã nhận dạng tương ứng với Unused sản phẩm, nhờ đó bạn có thể phân biệt các khối với nhau. StableHLO không có các thao tác nhảy, nên phần tương ứng của cú pháp MLIR không được dùng (nhưng vẫn có ở đó).

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

Thuộc tính đầu vào có tên và giá trị là một trong các hằng số được hỗ trợ. Đây là cách chính để chỉ định siêu dữ liệu tĩnh cho các phần tử chương trình. Ví dụ: thao tác concatenate sử dụng thuộc tính dimension để chỉ định phương diện mà theo đó các giá trị đầu vào được nối. Tương tự, slice op sử dụng nhiều thuộc tính như start_indiceslimit_indices để chỉ định các ranh giới được dùng để cắt giá trị đầu vào.

Hiện tại, các chương trình StableHLO trong tự nhiên đôi khi chứa các thuộc tính không được mô tả trong tài liệu này. Trong tương lai, chúng tôi dự định sẽ đưa những thuộc tính này vào opset StableHLO hoặc cấm chúng xuất hiện trong các chương trình StableHLO. Trong thời gian chờ đợi, bạn có thể xem danh sách các thuộc tính này tại đây:

  • layout (#629).
  • mhlo.frontend_attributes (#628).
  • mhlo.sharding (#619).
  • output_operand_aliases (#740).
  • Siêu dữ liệu vị trí (#594).
OpSignature ::= '(' [ValueType {',' ValueType}] ')' '->' '(' [ValueType {',' ValueType}] ')'

Chữ ký Op bao gồm các loại của tất cả giá trị đầu vào (danh sách các loại ở phía bên trái của ->) và các loại của tất cả giá trị đầu ra (danh sách các loại ở phía bên phải của ->). Nói một cách chính xác, các loại đầu vào là dư thừa và các loại đầu ra cũng gần như luôn dư thừa (vì đối với hầu hết các hoạt động StableHLO, các loại đầu ra có thể được suy luận từ đầu vào). Tuy nhiên, op signature là một phần trong cú pháp StableHLO để tương thích với MLIR.

Sau đây là một ví dụ về thao tác có mã nhớ là select_and_scatter. Thao tác này sử dụng 3 giá trị đầu vào (%operand, %source%init_value), 2 hàm đầu vào và 3 thuộc tính đầu vào (window_dimensions, window_stridespadding). Lưu ý cách chữ ký của thao tác chỉ bao gồm các loại giá trị đầu vào (nhưng không bao gồm các loại hàm và thuộc tính đầu vào được cung cấp nội tuyến).

%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>

Hằng số

Constant ::= BooleanConstant
           | IntegerConstant
           | FloatConstant
           | ComplexConstant
           | TensorConstant
           | QuantizedTensorConstant
           | StringConstant
           | EnumConstant

Hằng số StableHLO có một chữ và một loại, cùng nhau biểu thị một giá trị StableHLO. Nhìn chung, kiểu này là một phần của cú pháp hằng số, ngoại trừ khi kiểu này không rõ ràng (ví dụ: hằng số boolean có kiểu i1 không rõ ràng, trong khi hằng số nguyên có thể có nhiều kiểu có thể có).

BooleanConstant ::= BooleanLiteral
BooleanLiteral  ::= 'true' | 'false'

Hằng số boolean biểu thị các giá trị boolean truefalse. Hằng số Boolean có kiểu i1.

IntegerConstant   ::= IntegerLiteral ':' IntegerType
IntegerLiteral    ::= ['-' | '+'] DecimalDigits
                    | ['-' | '+'] '0x' HexadecimalDigits
DecimalDigits     ::= decimalDigit {decimalDigit}
HexadecimalDigits ::= hexadecimalDigit {hexadecimalDigit}
decimalDigit      ::= '0' | ... | '9'
hexadecimalDigit  ::= decimalDigit | 'a' | ... | 'f' | 'A' | ... | 'F'

Hằng số nguyên biểu thị các giá trị nguyên thông qua các chuỗi sử dụng ký hiệu thập phân hoặc thập lục phân. Các cơ số khác, chẳng hạn như cơ số nhị phân hoặc cơ số bát phân, không được hỗ trợ. Hằng số nguyên có các ràng buộc sau:

  • (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]

Hằng số dấu phẩy động biểu thị các giá trị dấu phẩy động thông qua các chuỗi sử dụng ký hiệu thập phân hoặc khoa học. Ngoài ra, bạn có thể dùng ký hiệu thập lục phân để chỉ định trực tiếp các bit cơ bản ở định dạng dấu phẩy động của loại tương ứng. Hằng số dấu phẩy động có các ràng buộc sau:

  • (C1) Nếu bạn sử dụng ký hiệu không phải thập lục phân, hãy dùng is_wellformed(float_literal, float_type).
  • (C2) Nếu sử dụng ký hiệu thập lục phân, hãy dùng size(hexadecimal_digits) = num_bits(float_type) / 4.
ComplexConstant ::= ComplexLiteral ':' ComplexType
ComplexLiteral  ::= '(' RealPart ',' ImaginaryPart ')'
RealPart        ::= FloatLiteral
ImaginaryPart   ::= FloatLiteral

Hằng số phức biểu thị các giá trị phức bằng cách sử dụng danh sách gồm phần thực (xuất hiện đầu tiên) và phần ảo (xuất hiện thứ hai). Ví dụ: (1.0, 0.0) : complex<f32> biểu thị 1.0 + 0.0i(0.0, 1.0) : complex<f32> biểu thị 0.0 + 1.0i. Thứ tự mà các phần này được lưu trữ trong bộ nhớ là do quá trình triển khai xác định. Hằng số phức có các ràng buộc sau:

  • (C1) is_wellformed(real_part, complex_element_type(complex_type)).
  • (C2) is_wellformed(imaginary_part, complex_element_type(complex_type)).
TensorConstant ::= TensorLiteral ':' TensorType
TensorLiteral  ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'
DenseLiteral   ::= DenseDimension | DenseElements
DenseDimension ::= '[' [DenseLiteral {',' DenseLiteral}] ']'
DenseElements  ::= [ElementLiteral {',' ElementLiteral}]
ElementLiteral ::= BooleanLiteral | IntegerLiteral | FloatLiteral | ComplexLiteral

Hằng số tensor biểu thị các giá trị tensor bằng cách sử dụng các danh sách lồng nhau được chỉ định thông qua ký pháp NumPy. Ví dụ: dense<[[1, 2, 3], [4, 5, 6]]> : tensor<2x3xi32> biểu thị một giá trị tensor có ánh xạ sau đây từ chỉ mục đến các phần tử: {0, 0} => 1, {0, 1} => 2, {0, 2} => 3, {1, 0} => 4, {1, 1} => 5, {1, 2} => 6. Thứ tự mà các phần tử này được lưu trữ trong bộ nhớ sau đó sẽ do quá trình triển khai xác định. Hằng số Tensor có các ràng buộc sau:

  • (C1) has_syntax(tensor_literal, element_type(tensor_type)), trong đó:
    • 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)), trong đó:
    • 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:]).
    • nếu không, hãy nói false.
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral  ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'

Hằng số tensor được lượng tử hoá biểu thị các giá trị tensor được lượng tử hoá bằng cách sử dụng cùng một ký hiệu như hằng số tensor, với các phần tử được chỉ định là hằng số của loại lưu trữ. Các hằng số tensor được lượng tử hoá có các ràng buộc sau:

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

Chuỗi ký tự bao gồm các byte được chỉ định bằng cách sử dụng ký tự ASCII và chuỗi thoát. Chúng không phụ thuộc vào phương thức mã hoá, vì vậy, cách diễn giải các byte này do quá trình triển khai xác định. Hằng chuỗi có loại string.

Ops

Giá trị tuyệt đối

Ngữ nghĩa

Thực hiện phép toán abs theo phần tử trên tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số nguyên có dấu: mô đun số nguyên.
  • Đối với số thực: abs từ IEEE-754.
  • Đối với số phức: mô-đun phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(abs, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor của số nguyên có dấu, dấu phẩy động hoặc loại phức hợp hoặc tensor lượng tử hoá trên mỗi tensor (C1-C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên có dấu hoặc dấu phẩy động hoặc tensor lượng tử hoá trên mỗi tensor (C1-C2)

Giới hạn

  • (C1) shape(result) = shape(operand).
  • (C2) baseline_element_type(result) được xác định là:
    • complex_element_type(element_type(operand)) nếu is_complex(operand).
    • Nếu không thì là baseline_element_type(operand).

Ví dụ

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

 Các ví dụ khác

thêm

Ngữ nghĩa

Thực hiện phép cộng theo phần tử của hai tensor lhsrhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với giá trị boolean: OR logic.
  • Đối với số nguyên: phép cộng số nguyên.
  • Đối với số thực: addition từ IEEE-754.
  • Đối với số phức: phép cộng số phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(add, lhs, rhs, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor hoặc tensor được lượng tử hoá (C1-C6)
(I2) rhs tensor hoặc tensor được lượng tử hoá (C1-C5), (C7)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá (C1 – C7)

Giới hạn

  • Nếu thao tác sử dụng các tensor không được định lượng:
    • (C1) type(lhs) = type(rhs) = type(result).
  • Nếu thao tác sử dụng các tensor được định lượng:
    • (C2) is_quantized(lhs) and is_quantized(rhs) and is_quantized(result).
    • (C3) storage_type(lhs) = storage_type(rhs) = storage_type(result).
    • (C4) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result).
    • (C5) (is_per_axis_quantized(lhs) or is_per_axis_quantized(rhs)) = is_per_axis_quantized(result).
    • (C6) Nếu is_per_axis_quantized(lhs), thì quantization_dimension(lhs) = quantization_dimension(result).
    • (C7) Nếu is_per_axis_quantized(rhs), thì quantization_dimension(rhs) = quantization_dimension(result).

Ví dụ

// %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]]

 Các ví dụ khác

after_all

Ngữ nghĩa

Đảm bảo rằng các thao tác tạo ra inputs được thực thi trước mọi thao tác phụ thuộc vào result. Việc thực thi thao tác này không làm gì cả, nó chỉ tồn tại để thiết lập các phần phụ thuộc dữ liệu từ result đến inputs.

Thông tin đầu vào

Hãng nhạc Tên Loại
(I1) inputs số lượng token có thể thay đổi

Kết quả đầu ra

Tên Loại
result token

Ví dụ

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

 Các ví dụ khác

all_gather

Ngữ nghĩa

Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy nối các giá trị của tensor operands từ mỗi quy trình dọc theo all_gather_dim và tạo ra các tensor results.

Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:

  • cross_replica(replica_groups) nếu channel_id <= 0 and use_global_device_ids = false.
  • cross_replica_and_partition(replica_groups) nếu channel_id > 0 and use_global_device_ids = false.
  • flattened_ids(replica_groups) nếu channel_id > 0 and use_global_device_ids = true.

Sau đó, trong mỗi process_group:

  • operands...@receiver = [operand@sender for sender in process_group] cho tất cả receiver trong process_group.
  • results...@process = concatenate(operands...@process, all_gather_dim) cho tất cả process trong process_group.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operands số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C6)
(I2) all_gather_dim hằng số thuộc loại si64 (C1), (C6)
(I3) replica_groups Hằng số tensor 2 chiều thuộc loại si64 (C2-C4)
(I4) channel_id hằng số thuộc loại si64 (C5)
(I5) use_global_device_ids hằng số thuộc loại i1 (C5)

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C6)

Giới hạn

  • (C1) 0 <= all_gather_dim < rank(operands...).
  • (C2) is_unique(replica_groups).
  • (C3) size(replica_groups) được xác định là:
    • num_replicas nếu bạn sử dụng cross_replica.
    • num_replicas nếu bạn sử dụng cross_replica_and_partition.
    • num_processes nếu bạn sử dụng flattened_ids.
  • (C4) 0 <= replica_groups < size(replica_groups).
  • (C5) Nếu use_global_device_ids = true, thì channel_id > 0.
  • (C6) type(results...) = type(operands...) ngoại trừ:
    • dim(results..., all_gather_dim) = dim(operands..., all_gather_dim) * dim(process_groups, 1).

Ví dụ

// 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]]

 Các ví dụ khác

all_reduce

Ngữ nghĩa

Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy áp dụng một hàm rút gọn computation cho các giá trị của tensor operands từ mỗi quy trình và tạo ra các tensor results.

Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:

  • cross_replica(replica_groups) nếu channel_id <= 0 and use_global_device_ids = false.
  • cross_replica_and_partition(replica_groups) nếu channel_id > 0 and use_global_device_ids = false.
  • flattened_ids(replica_groups) nếu channel_id > 0 and use_global_device_ids = true.

Sau đó, trong mỗi process_group:

  • results...@process[result_index] = exec(schedule) cho một số cây nhị phân schedule trong đó:
    • exec(node) = computation(exec(node.left), exec(node.right)).
    • exec(leaf) = leaf.value.
  • schedule là một cây nhị phân do quá trình triển khai xác định, trong đó thứ tự duyệt là to_destination_type(operands...@process_group...[result_index], type(func_inputs(computation)[0])).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operands số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C5), (C6)
(I2) replica_groups số lượng biến đổi của hằng số tensor 1 chiều thuộc loại si64 (C1-C3)
(I3) channel_id hằng số thuộc loại si64 (C4)
(I4) use_global_device_ids hằng số thuộc loại i1 (C4)
(I5) computation hàm (C5)

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C6-C7)

Giới hạn

  • (C1) is_unique(replica_groups).
  • (C2) size(replica_groups) được xác định là:
    • num_replicas nếu bạn sử dụng cross_replica.
    • num_replicas nếu bạn sử dụng cross_replica_and_partition.
    • num_processes nếu bạn sử dụng flattened_ids.
  • (C3) 0 <= replica_groups < size(replica_groups).
  • (C4) Nếu use_global_device_ids = true, thì channel_id > 0.
  • (C5) computation có loại (tensor<E>, tensor<E>) -> (tensor<E>) trong đó is_promotable(element_type(operand), E).
  • (C6) shape(results...) = shape(operands...).
  • (C7) element_type(results...) = E.

Ví dụ

// 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]

 Các ví dụ khác

all_to_all

Ngữ nghĩa

all_to_all

Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy chia các giá trị của tensor operands dọc theo split_dimension thành các phần, phân tán các phần đã chia giữa các quy trình, nối các phần đã phân tán dọc theo concat_dimension và tạo ra các tensor results. Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:

  • cross_replica(replica_groups) nếu channel_id <= 0.
  • cross_partition(replica_groups) nếu channel_id > 0.

Sau đó, trong mỗi process_group:

  • split_parts...@sender = split(operands...@sender, split_count, split_dimension) cho tất cả sender trong process_group.
  • scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group] nơi receiver_index = process_group.index(receiver).
  • results...@process = concatenate(scattered_parts...@process, concat_dimension).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operands số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C1 – C3), (C9)
(I2) split_dimension hằng số thuộc loại si64 (C1), (C2), (C9)
(I3) concat_dimension hằng số thuộc loại si64 (C3), (C9)
(I4) split_count hằng số thuộc loại si64 (C2), (C4), (C8), (C9)
(I5) replica_groups Hằng số tensor 2 chiều thuộc loại si64 (C5-C8)
(I6) channel_id hằng số thuộc loại si64

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C9)

Giới hạn

  • (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) được xác định là:
    • num_replicas nếu bạn sử dụng cross_replica.
    • num_partitions nếu bạn sử dụng cross_partition.
  • (C7) 0 <= replica_groups < size(replica_groups).
  • (C8) dim(replica_groups, 1) = split_count.
  • (C9) type(results...) = type(operands...) ngoại trừ trường hợp split_dimension != concat_dimension:
    • dim(results..., split_dimension) = dim(operands..., split_dimension) / split_count.
    • dim(results..., concat_dimension) = dim(operands..., concat_dimension) * split_count.

Ví dụ

// 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]]

 Các ví dụ khác

Ngữ nghĩa

Thực hiện phép toán AND theo phần tử của hai tensor lhsrhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với giá trị boolean: phép toán AND logic.
  • Đối với số nguyên: bitwise AND.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại boolean hoặc số nguyên (C1)
(I2) rhs tensor thuộc loại boolean hoặc số nguyên (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại boolean hoặc số nguyên (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

atan2

Ngữ nghĩa

Thực hiện phép toán atan2 theo phần tử trên tensor lhsrhs, đồng thời tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: atan2 từ IEEE-754.
  • Đối với số phức: atan2 phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(atan2, lhs, rhs, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)
(I2) rhs tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

batch_norm_grad

Ngữ nghĩa

Tính toán độ dốc của một số đầu vào của batch_norm_training truyền ngược từ grad_output và tạo ra các tensor grad_operand, grad_scalegrad_offset. Cụ thể hơn, thao tác này có thể được biểu thị dưới dạng một thao tác phân tách thành các thao tác StableHLO hiện có bằng cách sử dụng cú pháp Python như sau:

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

Đối với các loại được định lượng, hãy thực hiện 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)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1-C3), (C5)
(I2) scale Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor (C2), (C4), (C5)
(I3) mean Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor (C2), (C4)
(I4) variance Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor (C2), (C4)
(I5) grad_output tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C2), (C3)
(I6) epsilon hằng số thuộc loại f32
(I7) feature_index hằng số thuộc loại si64 (C1), (C5)

Kết quả đầu ra

Tên Loại Giới hạn
grad_operand tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C2), (C3)
grad_scale Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor (C2), (C4)
grad_offset Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor (C2), (C4)

Giới hạn

  • (C1) 0 <= feature_index < rank(operand).
  • (C2) operand, scale, mean, variance, grad_output, grad_operand, grad_scalegrad_offset có cùng baseline_element_type.
  • (C3) operand, grad_outputgrad_operand có cùng hình dạng.
  • (C4) scale, mean, variance, grad_scalegrad_offset có cùng hình dạng.
  • (C5) size(scale) = dim(operand, feature_index).

Ví dụ

// %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

Ngữ nghĩa

Chuẩn hoá tensor operand trên tất cả các phương diện, ngoại trừ phương diện feature_index và tạo ra một tensor result. Theo cách chính thức hơn, thao tác này có thể được biểu thị dưới dạng một thao tác phân tách thành các thao tác StableHLO hiện có bằng cách sử dụng cú pháp Python như sau:

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)

Đối với các loại được định lượng, hãy thực hiện 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)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1 – C7)
(I2) scale Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor (C2), (C3)
(I3) offset Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor (C2), (C4)
(I4) mean Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor (C5)
(I5) variance Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor (C2), (C6)
(I6) epsilon hằng số thuộc loại f32
(I7) feature_index hằng số thuộc loại si64 (C1), (C3-C6)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C2), (C7)

Giới hạn

  • (C1) 0 <= feature_index < rank(operand).
  • (C2) operand, scale, offset, mean, varianceresult có cùng baseline_element_type.
  • (C3) size(scale) = dim(operand, feature_index).
  • (C4) size(offset) = dim(operand, feature_index).
  • (C5) size(mean) = dim(operand, feature_index).
  • (C6) size(variance) = dim(operand, feature_index).
  • (C7) baseline_type(operand) = baseline_type(result).

Ví dụ

// %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

Ngữ nghĩa

Tính toán giá trị trung bình và phương sai trên tất cả các phương diện, ngoại trừ phương diện feature_index và chuẩn hoá tenxơ operand tạo ra các tenxơ output, batch_meanbatch_var. Cụ thể hơn, bạn có thể biểu thị thao tác này dưới dạng một thao tác phân tách thành các thao tác StableHLO hiện có bằng cách sử dụng cú pháp Python như sau:

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

Đối với các loại được định lượng, hãy thực hiện 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)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)
(I2) scale Tensor 1 chiều của dấu phẩy động hoặc được lượng tử hoá theo từng tensor (C2), (C3)
(I3) offset Tensor 1 chiều của dấu phẩy động hoặc được lượng tử hoá theo từng tensor (C2), (C4)
(I4) epsilon hằng số thuộc loại f32 (C1), (C3-C6)
(I5) feature_index hằng số thuộc loại si64 (C1), (C3-C6)

Kết quả đầu ra

Tên Loại Giới hạn
output tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C7)
batch_mean Tensor 1 chiều của dấu phẩy động hoặc được lượng tử hoá theo từng tensor (C2), (C5)
batch_var Tensor 1 chiều của dấu phẩy động hoặc được lượng tử hoá theo từng tensor (C2), (C6)

Giới hạn

  • (C1) 0 <= feature_index < rank(operand).
  • (C2) operand, scale, offset, batch_mean, batch_varoutput có cùng baseline_element_type.
  • (C3) size(scale) = dim(operand, feature_index).
  • (C4) size(offset) = dim(operand, feature_index).
  • (C5) size(batch_mean) = dim(operand, feature_index).
  • (C6) size(batch_var) = dim(operand, feature_index).
  • (C7) baseline_type(output) = baseline_type(operand).

Ví dụ

// %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

Ngữ nghĩa

Thực hiện một thao tác bitcast trên tenxơ operand và tạo ra một tenxơ result, trong đó các bit của toàn bộ tenxơ operand được diễn giải lại bằng cách sử dụng kiểu của tenxơ result.

Cụ thể hơn, cho E = element_type(operand), E' = element_type(result)R = rank(operand):

  • Nếu num_bits(E') < num_bits(E), bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1]).
  • Nếu num_bits(E') > num_bits(E), bits(result[i0, ..., iR-2]) = bits(operand[i0, ..., iR-2, :]).
  • Nếu num_bits(E') = num_bits(E), bits(result[i0, ..., iR-1]) = bits(operand[i0, ..., iR-1]).

bits trả về biểu thị trong bộ nhớ của một giá trị nhất định và hành vi của giá trị này được xác định theo cách triển khai vì biểu thị chính xác của các tensor được xác định theo cách triển khai và biểu thị chính xác của các loại phần tử cũng được xác định theo cách triển khai.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor được lượng tử hoá (C1-C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá (C1-C2)

Giới hạn

  • (C1) Cho E = is_quantized(operand) ? storage_type(operand) : element_type(operand), E' = is_quantized(result) ? storage_type(result) : element_type(result)R = rank(operand):
    • Nếu num_bits(E') = num_bits(E), shape(result) = shape(operand).
    • Nếu num_bits(E') < num_bits(E):
    • rank(result) = R + 1.
    • dim(result, i) = dim(operand, i) cho tất cả 0 <= i < R.
    • dim(result, R) * num_bits(E') = num_bits(E).
    • Nếu num_bits(E') > num_bits(E):
    • rank(result) = R - 1.
    • dim(result, i) = dim(operand, i) cho tất cả 0 <= i < R.
    • dim(operand, R - 1) * num_bits(E) = num_bits(E').
  • (C2) Nếu is_complex(operand) or is_complex(result), thì is_complex(operand) and is_complex(result).

Ví dụ

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

 Các ví dụ khác

broadcast_in_dim

Ngữ nghĩa

broadcast_in_dim

Mở rộng phương diện và/hoặc thứ hạng của một tensor đầu vào bằng cách sao chép dữ liệu trong tensor operand và tạo ra một tensor result. Cụ thể hơn, result[result_index] = operand[operand_index] trong đó với mọi d trong axes(operand):

  • operand_index[d] = 0 nếu dim(operand, d) = 1.
  • Nếu không thì là operand_index[d] = result_index[broadcast_dimensions[d]].

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor được lượng tử hoá (C1-C2), (C5-C6)
(I2) broadcast_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C2-C6)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá (C1), (C3), (C5-C6)

Giới hạn

  • (C1) element_type(result) được cho bởi:
    • element_type(operand), nếu !is_per_axis_quantized(operand).
    • element_type(operand) ngoại trừ quantization_dimension(operand), scales(operand)zero_points(operand) có thể khác với quantization_dimension(result), scales(result)zero_points(result) tương ứng, nếu không.
  • (C2) size(broadcast_dimensions) = rank(operand).
  • (C3) 0 <= broadcast_dimensions < rank(result).
  • (C4) is_unique(broadcast_dimensions).
  • (C5) Đối với tất cả daxes(operand):
    • dim(operand, d) = 1 hoặc
    • dim(operand, d) = dim(result, broadcast_dimensions[d]).
  • (C6) Nếu is_per_axis_quantized(result):
    • quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)].
    • Nếu dim(operand, quantization_dimension(operand)) = 1, thì scales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result))).

Ví dụ

// %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]
//            ]
//          ]

 Các ví dụ khác

ốp lưng

Ngữ nghĩa

Tạo ra kết quả đầu ra từ việc thực thi chính xác một hàm từ branches tuỳ thuộc vào giá trị của index. Cụ thể hơn, result = selected_branch() trong đó:

  • selected_branch = branches[index] nếu 0 <= index < size(branches).
  • Nếu không thì là selected_branch = branches[-1].

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) index Tensor 0 chiều thuộc loại si32
(I2) branches số lượng hàm có thể thay đổi (C1-C4)

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor, tensor lượng tử hoá hoặc mã thông báo có thể thay đổi (C4)

Giới hạn

  • (C1) 0 < size(branches).
  • (C2) input_types(branches...) = [].
  • (C3) same(output_types(branches...)).
  • (C4) type(results...) = output_types(branches[0]).

Ví dụ

// %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]

 Các ví dụ khác

cbrt

Ngữ nghĩa

Thực hiện phép toán căn bậc ba theo phần tử trên tensor operand và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: rootn(x, 3) từ IEEE-754.
  • Đối với số phức: căn bậc ba của số phức.
  • Đối với các loại được định lượng: dequantize_op_quantize(cbrt, operand, type(result))

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

ceil

Ngữ nghĩa

Thực hiện thao tác làm tròn lên từng phần tử của tensor operand và tạo ra một tensor result. Triển khai thao tác roundToIntegralTowardPositive theo quy cách IEEE-754. Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(ceil, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

cholesky

Ngữ nghĩa

Tính toán phân tích Cholesky của một lô ma trận.

Cụ thể hơn, đối với tất cả i trong index_space(result), result[i0, ..., iR-3, :, :] là một phép phân tích Cholesky của a[i0, ..., iR-3, :, :], dưới dạng ma trận tam giác dưới (nếu lowertrue) hoặc ma trận tam giác trên (nếu lowerfalse). Các giá trị đầu ra trong tam giác đối diện, tức là tam giác trên nghiêm ngặt hoặc tam giác dưới nghiêm ngặt tương ứng, được xác định theo cách triển khai.

Nếu có i trong đó ma trận đầu vào không phải là ma trận xác định dương Hermitian, thì hành vi này không xác định.

Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) a tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1-C3)
(I2) lower hằng số thuộc loại i1

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

  • (C1) baseline_type(a) = baseline_type(result).
  • (C2) 2 <= rank(a).
  • (C3) dim(a, -2) = dim(a, -1).

Ví dụ

// %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]
//          ]

kẹp

Ngữ nghĩa

Kẹp mọi phần tử của tensor operand trong khoảng từ giá trị tối thiểu đến tối đa và tạo ra tensor result. Cụ thể hơn, result[result_index] = minimum(maximum(operand[result_index], min_element), max_element), trong đó min_element = rank(min) = 0 ? min[] : min[result_index], max_element = rank(max) = 0 ? max[] : max[result_index]. Đối với các loại được lượng tử hoá, hãy thực hiện dequantize_op_quantize(clamp, min, operand, max, type(result)).

Việc áp đặt một thứ tự cho các số phức liên quan đến ngữ nghĩa đáng ngạc nhiên, vì vậy trong tương lai, chúng tôi dự định xoá hỗ trợ cho các số phức đối với thao tác này (#560).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) min tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C3)
(I2) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1-C4)
(I3) max tensor hoặc tensor lượng tử hoá trên mỗi tensor (C2), (C3)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C4)

Giới hạn

  • (C1) rank(min) = 0 or shape(min) = shape(operand).
  • (C2) rank(max) = 0 or shape(max) = shape(operand).
  • (C3) baseline_element_type(min) = baseline_element_type(operand) = baseline_element_type(max).
  • (C4) baseline_type(operand) = baseline_type(result).

Ví dụ

// %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]

 Các ví dụ khác

collective_broadcast

Ngữ nghĩa

Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy gửi giá trị của tensor operand từ quy trình nguồn đến các quy trình đích và tạo ra một tensor result.

Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:

  • cross_replica(replica_groups) nếu channel_id <= 0.
  • cross_partition(replica_groups) nếu channel_id > 0.

Sau đó, result@process được tính theo công thức:

  • operand@process_groups[i, 0] nếu có một i sao cho quy trình này nằm trong process_groups[i].
  • broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result)) nếu không.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor được lượng tử hoá theo từng tensor (C3)
(I2) replica_groups số lượng biến đổi của hằng số tensor 1 chiều thuộc loại si64 (C1), (C2)
(I3) channel_id hằng số thuộc loại si64

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá theo từng tensor (C3)

Giới hạn

  • (C1) is_unique(replica_groups).
  • (C2) 0 <= replica_groups < N trong đó N được xác định là:
    • num_replicas nếu bạn sử dụng cross_replica.
    • num_partitions nếu bạn sử dụng cross_partition.
  • (C3) type(result) = type(operand).

Ví dụ

// 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

Ngữ nghĩa

Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy gửi giá trị của tensor operand từ quy trình nguồn đến quy trình đích và tạo ra một tensor result.

Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:

  • cross_replica(source_target_pairs) nếu channel_id <= 0.
  • cross_partition(source_target_pairs) nếu channel_id > 0.

Sau đó, result@process được tính theo công thức:

  • operand@process_groups[i, 0], nếu tồn tại một i sao cho process_groups[i, 1] = process.
  • broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result)) nếu không.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C5)
(I2) source_target_pairs Hằng số tensor 2 chiều thuộc loại si64 (C1-C4)
(I3) channel_id hằng số thuộc loại si64

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

  • (C1) dim(source_target_pairs, 1) = 2.
  • (C2) is_unique(source_target_pairs[:, 0]).
  • (C3) is_unique(source_target_pairs[:, 1]).
  • (C4) 0 <= source_target_pairs < N, trong đó N được xác định là:
    • num_replicas nếu bạn sử dụng cross_replica.
    • num_partitions nếu bạn sử dụng cross_partition.
  • (C5) type(result) = type(operand).

Ví dụ

// 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]]

 Các ví dụ khác

so sánh

Ngữ nghĩa

Thực hiện so sánh từng phần tử của các tensor lhsrhs theo comparison_directioncompare_type, đồng thời tạo ra một tensor result.

Giá trị của comparison_directioncompare_type có ngữ nghĩa sau:

Đối với các loại phần tử boolean và số nguyên:

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

Đối với các loại phần tử dấu phẩy động có compare_type = FLOAT, thao tác này sẽ triển khai các thao tác IEEE-754 sau:

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

Đối với các kiểu phần tử dấu phẩy động có compare_type = TOTALORDER, thao tác này sử dụng tổ hợp các thao tác totalOrdercompareQuietEqual từ IEEE-754.

Đối với các loại phần tử phức tạp, việc so sánh theo từ điển các cặp (real, imag) được thực hiện bằng cách sử dụng comparison_directioncompare_type được cung cấp. Việc áp đặt một thứ tự cho các số phức liên quan đến ngữ nghĩa đáng ngạc nhiên, vì vậy trong tương lai, chúng tôi dự định sẽ xoá hỗ trợ cho các số phức khi comparison_directionGE, GT, LE hoặc LT (#560).

Đối với các loại được định lượng, hãy thực hiện dequantize_compare(lhs, rhs, comparison_direction).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C1-C3)
(I2) rhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C1-C2)
(I3) comparison_direction enum của EQ, NE, GE, GT, LELT
(I4) compare_type enum của FLOAT, TOTALORDER, SIGNEDUNSIGNED (C3)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại boolean (C2)

Giới hạn

  • (C1) baseline_element_type(lhs) = baseline_element_type(rhs).
  • (C2) shape(lhs) = shape(rhs) = shape(result).
  • (C3) compare_type được xác định là:
    • SIGNED nếu is_signed_integer(element_type(lhs)).
    • UNSIGNED nếu is_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs)).
    • FLOAT hoặc TOTALORDER nếu is_float(element_type(lhs)).
    • FLOAT nếu is_complex(element_type(lhs)).

Ví dụ

// %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]

 Các ví dụ khác

phức tạp

Ngữ nghĩa

Thực hiện quá trình chuyển đổi theo phần tử thành một giá trị phức tạp từ một cặp giá trị thực và giá trị ảo, lhsrhs, đồng thời tạo ra một tenxơ result.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại f32 hoặc f64 (C1-C3)
(I2) rhs tensor thuộc loại f32 hoặc f64 (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại phức (C2), (C3)

Giới hạn

  • (C1) type(lhs) = type(rhs).
  • (C2) shape(result) = shape(lhs).
  • (C3) element_type(result) có loại complex<E> trong đó E = element_type(lhs).

Ví dụ

// %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)]

 Các ví dụ khác

tổng hợp

Ngữ nghĩa

Đóng gói một thao tác được tạo thành từ các thao tác StableHLO khác, lấy inputscomposite_attributes, đồng thời tạo ra results. Ngữ nghĩa của thao tác được triển khai bằng thuộc tính decomposition. Bạn có thể thay thế thao tác composite bằng thao tác phân tách mà không cần thay đổi ngữ nghĩa của chương trình. Trong trường hợp việc nội tuyến quá trình phân tách không cung cấp cùng một ngữ nghĩa op, hãy ưu tiên sử dụng custom_call.

Trường version (mặc định là 0) dùng để biểu thị thời điểm ngữ nghĩa của một thành phần thay đổi.

Thông tin đầu vào

Hãng nhạc Tên Loại
(I1) inputs số lượng giá trị thay đổi
(I2) name hằng số thuộc loại string
(I3) composite_attributes từ điển thuộc tính
(I4) decomposition hằng số thuộc loại string
(I5) version hằng số thuộc loại si32

Kết quả đầu ra

Tên Loại
results số lượng giá trị thay đổi

Giới hạn

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

Ví dụ

%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>

 Các ví dụ khác

nối

Ngữ nghĩa

Nối inputs dọc theo phương diện dimension theo cùng một thứ tự như các đối số đã cho và tạo ra một tenxơ result. Một cách chính thức hơn, result[i0, ..., id, ..., iR-1] = inputs[k][i0, ..., kd, ..., iR-1], trong đó:

  1. id = d0 + ... + dk-1 + kd.
  2. d bằng dimensiond0, ... là kích thước thứ nguyên thứ d của inputs.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) inputs số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C1-C6)
(I2) dimension hằng số thuộc loại si64 (C2), (C4), (C6)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C5-C6)

Giới hạn

  • (C1) same(element_type(inputs...)).
  • (C2) same(shape(inputs...)) ngoại trừ 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]) ngoại trừ:
    • dim(result, dimension) = dim(inputs[0], dimension) + ....

Ví dụ

// %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]]

 Các ví dụ khác

hằng số

Ngữ nghĩa

Tạo một tensor output từ một hằng số value.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) value hằng số (C1)

Kết quả đầu ra

Tên Loại Giới hạn
output tensor hoặc tensor được lượng tử hoá (C1)

Giới hạn

  • (C1) type(value) = type(output).

Ví dụ

%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]]

 Các ví dụ khác

chuyển đổi

Ngữ nghĩa

Thực hiện quá trình chuyển đổi từng phần tử từ một loại phần tử sang một loại phần tử khác trên tensor operand và tạo ra một tensor result.

Đối với các lượt chuyển đổi boolean-to-any-supported-type, giá trị false được chuyển đổi thành 0 và giá trị true được chuyển đổi thành 1. Đối với các lượt chuyển đổi any-supported-type-to-boolean, giá trị 0 sẽ được chuyển đổi thành false và các giá trị khác 0 sẽ được chuyển đổi thành true. Hãy xem bên dưới để biết cách hoạt động này đối với các loại phức tạp.

Đối với các lượt chuyển đổi liên quan đến số nguyên sang số nguyên, số nguyên sang dấu phẩy động hoặc dấu phẩy động sang dấu phẩy động, nếu giá trị nguồn có thể được biểu thị chính xác trong loại đích, thì giá trị kết quả sẽ là giá trị biểu thị chính xác đó. Nếu không, hành vi này sẽ được xác định sau (#180).

Đối với các lượt chuyển đổi liên quan đến floating-point-to-integer, phần thập phân sẽ bị cắt bớt. Nếu giá trị bị cắt không thể được biểu thị trong loại đích đến, thì hành vi sẽ là TBD (#180).

Quá trình chuyển đổi liên quan đến số phức sang số phức tuân theo cùng một hành vi của quá trình chuyển đổi số thực sang số thực để chuyển đổi các phần thực và phần ảo.

Đối với các lượt chuyển đổi complex-to-any-other-typeany-other-type-to-complex, giá trị ảo nguồn sẽ bị bỏ qua hoặc giá trị ảo đích sẽ được đặt về 0, tương ứng. Việc chuyển đổi phần thực tuân theo các quy tắc chuyển đổi số thực dấu phẩy động.

Về nguyên tắc, thao tác này có thể biểu thị việc khử lượng tử hoá (chuyển đổi từ các tensor được lượng tử hoá thành các tensor thông thường), lượng tử hoá (chuyển đổi từ các tensor thông thường thành các tensor được lượng tử hoá) và lượng tử hoá lại (chuyển đổi giữa các tensor được lượng tử hoá), nhưng hiện tại chúng ta có các thao tác chuyên dụng cho việc đó – uniform_dequantize cho trường hợp sử dụng đầu tiên và uniform_quantize cho trường hợp sử dụng thứ hai và thứ ba. Trong tương lai, hai thao tác này có thể được hợp nhất thành convert (#1576).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor (C1)

Giới hạn

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

Ví dụ

// %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)]

 Các ví dụ khác

tích chập

Ngữ nghĩa

Tính tích vô hướng giữa các cửa sổ của lhs và các lát của rhs, đồng thời tạo ra result. Sơ đồ sau đây cho thấy cách tính toán các phần tử trong result từ lhsrhs bằng một ví dụ cụ thể.

tích chập

Cụ thể hơn, hãy xem xét việc điều chỉnh lại các đầu vào sau đây theo lhs để có thể biểu thị các cửa sổ 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).

Việc điều chỉnh khung hình này sử dụng các hàm trợ giúp sau:

  • 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] nơi j[d] = i[permutation[d]].

Nếu feature_group_count = 1batch_group_count = 1, thì đối với tất cả output_spatial_index trong index_space(dim(result, output_spatial_dimensions...)), result[result_shape(:, output_spatial_index, :)] = dot_product trong đó:

  • 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]). Tính năng này có vẻ không được dùng, nên trong tương lai, chúng tôi dự định sẽ xoá tính năng này (#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]).

Nếu 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).

Nếu 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).

Đối với các loại được lượng tử hoá, hãy thực hiện 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)).

Đối với các loại được lượng tử hoá kết hợp, hãy thực hiện 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).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C1), (C10-C11), (C14) (C25), (C27-C28), (C31-C32), (C34)
(I2) rhs tensor hoặc tensor được lượng tử hoá (C1), (C14-C16), (C25), (C27-C29), (C31-C34)
(I3) window_strides Hằng số tensor 1 chiều thuộc loại si64 (C2-C3), (C25)
(I4) padding Hằng số tensor 2 chiều thuộc loại si64 (C4), (C25)
(I5) lhs_dilation Hằng số tensor 1 chiều thuộc loại si64 (C5-C6), (C25)
(I6) rhs_dilation Hằng số tensor 1 chiều thuộc loại si64 (C7 – C8), (C25)
(I7) window_reversal Hằng số tensor 1 chiều thuộc loại i1 (C9)
(I8) input_batch_dimension hằng số thuộc loại si64 (C10), (C13), (C25)
(I9) input_feature_dimension hằng số thuộc loại si64 (C11), (C13-C14)
(I10) input_spatial_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C12), (C13), (C25)
(I11) kernel_input_feature_dimension hằng số thuộc loại si64 (C14), (C18)
(I12) kernel_output_feature_dimension hằng số thuộc loại si64 (C15-C16), (C18), (C25), (C29)
(I13) kernel_spatial_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C17-C18), (C25)
(I14) output_batch_dimension hằng số thuộc loại si64 (C20), (C25)
(I15) output_feature_dimension hằng số thuộc loại si64 (C20), (C25), (C30)
(I16) output_spatial_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C19-C20), (C25)
(I17) feature_group_count hằng số thuộc loại si64 (C11), (C14), (C16), (C21), (C23)
(I18) batch_group_count hằng số thuộc loại si64 (C10), (C15), (C22), (C23), (C25)
(I19) precision_config số lượng enum có thể thay đổi của DEFAULT, HIGHHIGHEST (C24)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá (C25-C28), (C30), (C32-34)

Giới hạn

  • (C1) N = rank(lhs) = rank(rhs).
  • (C2) size(window_strides) = N - 2.
  • (C3) 0 < window_strides.
  • (C4) shape(padding) = [N - 2, 2].
  • (C5) size(lhs_dilation) = N - 2.
  • (C6) 0 < lhs_dilation.
  • (C7) size(rhs_dilation) = N - 2.
  • (C8) 0 < rhs_dilation.
  • (C9) size(window_reversal) = N - 2.
  • (C10) dim(lhs, input_batch_dimension) % batch_group_count = 0.
  • (C11) dim(lhs, input_feature_dimension) % feature_group_count = 0.
  • (C12) size(input_spatial_dimensions) = N - 2.
  • (C13) Cho 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) Điều kiện 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) Cho 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) được xác định là:
    • dim(lhs, input_batch_dimension) / batch_group_count nếu result_dim = output_batch_dimension.
    • dim(rhs, kernel_output_feature_dimension) nếu result_dim = output_feature_dimension.
    • num_windows nếu không, trong đó:
    • 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.
  • Nếu thao tác sử dụng các tensor không được định lượng:
    • (C27) element_type(lhs) = element_type(rhs) = element_type(result).
  • Nếu thao tác sử dụng các tensor được định lượng:
    • (C28) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs).
    • (C29) Nếu is_per_axis_quantized(rhs), thì quantization_dimension(rhs) = kernel_output_feature_dimension.
    • (C30) Nếu is_per_axis_quantized(result), thì quantization_dimension(result) = output_feature_dimension.
    • Nếu is_quantized(lhs):
    • (C31) storage_type(lhs) = storage_type(rhs).
    • (C32) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result).
    • (C33) Nếu is_per_tensor_quantized(rhs), thì is_per_tensor_quantized(result).
    • Nếu !is_quantized(lhs):
    • (C34) element_type(lhs) = expressed_type(rhs) = element_type(result).

Ví dụ

// %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]]
//          ]]

 Các ví dụ khác

cosin

Ngữ nghĩa

Thực hiện phép toán cosin trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: cos từ IEEE-754.
  • Đối với số phức: cosin phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(cosine, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

count_leading_zeros

Ngữ nghĩa

Thực hiện việc đếm theo phần tử số lượng bit 0 ở đầu trong tenxơ operand và tạo ra một tenxơ result.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại số nguyên (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên (C1)

Giới hạn

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

Ví dụ

// %operand: [[0, 1], [128, -1]]
%result = "stablehlo.count_leading_zeros"(%operand) : (tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[64, 63], [56, 0]]

 Các ví dụ khác

custom_call

Ngữ nghĩa

Đóng gói một thao tác do quá trình triển khai xác định call_target_name nhận inputscalled_computations, đồng thời tạo ra results. has_side_effect, backend_configapi_version có thể được dùng để cung cấp siêu dữ liệu bổ sung do quá trình triển khai xác định.

Hiện tại, thao tác này chứa một tập hợp siêu dữ liệu khá lộn xộn, phản ánh sự phát triển tự nhiên của thao tác tương ứng trong trình biên dịch XLA. Trong tương lai, chúng tôi dự định sẽ hợp nhất siêu dữ liệu này (#741).

Thông tin đầu vào

Hãng nhạc Tên Loại
(I1) inputs số lượng giá trị thay đổi
(I2) call_target_name hằng số thuộc loại string
(I3) has_side_effect hằng số thuộc loại i1
(I4) backend_config hằng số thuộc loại string hoặc từ điển thuộc tính
(I5) api_version hằng số thuộc loại si32
(I6) called_computations số lượng hằng số có thể thay đổi thuộc loại string
(I7) output_operand_aliases chỉ định các phần bí danh trong đầu ra và toán hạng

Kết quả đầu ra

Tên Loại
results số lượng giá trị thay đổi

(Hỗ trợ GPU XLA) Các mục tiêu custom_call đặc biệt

Có 3 call_target_name đặc biệt liên quan đến các loại buffer: CreateBuffer tạo một buffer chưa được khởi chạy, Pin tạo một buffer đã được khởi chạy và Unpin giải phóng một buffer và trả về nội dung của buffer.

%uninitialized_buffer = "stablehlo.custom_call"() {
  call_target_name = "CreateBuffer",
  api_version = 4 : i32,
} : () -> memref<4xf64>

%initialized_buffer = "stablehlo.custom_call"(%init_value) {
  call_target_name = "Pin",
  api_version = 4 : i32,
} : (tensor<4xf64>) -> memref<4xf64>

%dealloc_buffer = "stablehlo.custom_call"(%initialized_buffer) {
  call_target_name = "Unpin",
  api_version = 4 : i32,
} : (memref<4xf64>) -> tensor<4xf64>

Email đại diện

Một số hoạt động custom_call có thể yêu cầu một phần trong đầu ra và một phần trong các toán hạng để dùng chung bộ nhớ. Bạn có thể biểu thị điều này qua output_operand_aliases. Biểu diễn cặp bí danh bao gồm một danh sách các chỉ mục bộ dữ liệu đầu ra biểu thị phần đầu ra và một operand_index cùng với một danh sách các chỉ mục bộ dữ liệu toán hạng biểu thị phần toán hạng. Danh sách các chỉ mục bộ dữ liệu đầu ra hoặc toán hạng sẽ trống nếu loại tương ứng không phải là loại tuple và có thể có độ dài tuỳ ý đối với loại bộ dữ liệu lồng nhau tuỳ ý. Điều này tương tự như phần biểu thị biệt hiệu XLA.

Phần đầu ra và phần đầu vào trong một cặp bí danh phải có cùng loại. Đối với các hoạt động custom_call không phải là lệnh gọi đến CreateBuffer, PinUnpin, toán hạng buffer có thể xuất hiện tối đa trong một cặp bí danh và đầu ra buffer phải xuất hiện trong một cặp bí danh.

Ví dụ

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

%updated_buffer = "stablehlo.custom_call"(%buffer) {
  call_target_name = "Update",
  api_version = 4 : i32,
  output_operand_aliases = [
    #stablehlo.output_operand_alias<output_tuple_indices = [],
      operand_index = 0,
      operand_tuple_indices = []>]
} : (memref<4xf64>) -> memref<4xf64>

chia

Ngữ nghĩa

Thực hiện phép chia từng phần tử của các tensor số bị chia lhs và số chia rhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số nguyên: phép chia số nguyên tạo ra thương số đại số mà mọi phần phân số đều bị loại bỏ.
  • Đối với số thực: division từ IEEE-754.
  • Đối với số phức: phép chia số phức.
  • Đối với các loại được lượng tử hoá:
    • dequantize_op_quantize(divide, lhs, rhs, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)
(I2) rhs tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

dot_general

Ngữ nghĩa

Tính tích vô hướng giữa các lát của lhs và các lát của rhs, đồng thời tạo ra một tenxơ result.

Cụ thể hơn, result[result_index] = dot_product, trong đó:

  • 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 trong đó 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)).

Đối với các loại được lượng tử hoá, hãy thực hiện 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)).

Đối với các loại được lượng tử hoá kết hợp, hãy thực hiện 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 kiểm soát sự đánh đổi giữa tốc độ và độ chính xác cho các phép tính trên các phần phụ trợ của bộ tăng tốc. Đây có thể là một trong những giá trị sau (hiện tại, ngữ nghĩa của các giá trị enum này chưa được chỉ định rõ ràng, nhưng chúng tôi dự định giải quyết vấn đề này trong #755):

  • DEFAULT: Tính toán nhanh nhất, nhưng mức độ xấp xỉ chính xác nhất so với số ban đầu.
  • HIGH: Tính toán chậm hơn, nhưng có độ chính xác cao hơn khi xấp xỉ với số ban đầu.
  • HIGHEST: Tính toán chậm nhất, nhưng xấp xỉ chính xác nhất với số ban đầu.

DotAlgorithm xác định các thuộc tính chính của thuật toán dùng để triển khai phép toán dot, đồng thời xác định độ chính xác. Nếu bạn đặt các trường thuộc tính thuật toán, thì precision_config phải là DEFAULT. DotAlgorithms không có giá trị mặc định, vì các thông số mặc định được xác định theo cách triển khai. Do đó, bạn có thể đặt tất cả các trường thuật toán dấu chấm thành None để chỉ định một thuật toán dấu chấm trống. Thay vào đó, thuật toán này sẽ sử dụng giá trị precision_config.

Các trường DotAlgorithm bao gồm:

  • lhs_precision_typerhs_precision_type, độ chính xác mà LHS và RHS của thao tác được làm tròn. Các loại độ chính xác độc lập với các loại bộ nhớ của dữ liệu đầu vào và đầu ra.
  • accumulation_type độ chính xác dùng để tích luỹ.
  • lhs_component_count, rhs_component_countnum_primitive_operations áp dụng khi chúng ta đang thực hiện một thuật toán phân tách LHS và/hoặc RHS thành nhiều thành phần và thực hiện nhiều thao tác "nguyên thuỷ" trên các giá trị đó – thường là để mô phỏng độ chính xác cao hơn (ví dụ: Khai thác kiểu dữ liệu Trí tuệ nhân tạo bfloat16 để tính toán có độ chính xác cao hơn: bf16_6x tf32_3x, v.v.). Đối với các thuật toán không có quá trình phân tách, bạn nên đặt các giá trị này thành 1.
  • allow_imprecise_accumulation để chỉ định xem có được phép tích luỹ với độ chính xác thấp hơn cho một số bước hay không (ví dụ: CUBLASLT_MATMUL_DESC_FAST_ACCUM).

Ví dụ về các thuộc tính 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}

Các phương thức triển khai sẽ quyết định những tổ hợp được hỗ trợ. Nói chung, người dùng StableHLO không đảm bảo rằng mỗi thuật toán đều được hỗ trợ trên từng loại trình tăng tốc. Nếu một thuật toán nhất định không được hỗ trợ, thì phải phát sinh lỗi thay vì quay lại một thuật toán thay thế. Quy trình xác minh StableHLO sẽ cung cấp quy trình xác minh nỗ lực tối đa, ngăn chặn các thuật toán không được hỗ trợ trên bất kỳ phần cứng nào.

Hãy xem xla_data.proto > Algorithm để biết một số giá trị thuật toán được hỗ trợ. Phiếu yêu cầu #2483 ghi lại kế hoạch tạo một tài liệu tập trung về các thuật toán được hỗ trợ theo phần phụ trợ.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C5-C6), (C9-C10), (C12-C14), (C17-C18), (C20)
(I2) rhs tensor hoặc tensor được lượng tử hoá (C7-C10), (C12-C20)
(I3) lhs_batching_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C1), (C3), (C5), (C9), (C12)
(I4) rhs_batching_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C1), (C4), (C7), (C9)
(I5) lhs_contracting_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C2), (C3), (C6), (C10)
(I6) rhs_contracting_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C2), (C4), (C8), (C10), (C16)
(I7) precision_config số lượng enum có thể thay đổi của DEFAULT, HIGHHIGHEST (C11), (C21)
(I8) lhs_precision_type FloatType hoặc TensorFloat32 (C21)
(I9) rhs_precision_type FloatType hoặc TensorFloat32 (C21)
(I10) accumulation_type FloatType hoặc TensorFloat32 (C21)
(I11) lhs_component_count hằng số thuộc loại si32 (C21), (C22)
(I12) rhs_component_count hằng số thuộc loại si32 (C21), (C23)
(I13) num_primitive_operations hằng số thuộc loại si32 (C21), (C24)
(I14) allow_imprecise_accumulation hằng số thuộc loại bool (C21)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá (C12), (C14), (C18-C20)

Giới hạn

  • (C1) size(lhs_batching_dimensions) = size(rhs_batching_dimensions).
  • (C2) size(lhs_contracting_dimensions) = size(rhs_contracting_dimensions).
  • (C3) is_unique(lhs_batching_dimensions + lhs_contracting_dimensions).
  • (C4) is_unique(rhs_batching_dimensions + rhs_contracting_dimensions).
  • (C5) 0 <= lhs_batching_dimensions < rank(lhs).
  • (C6) 0 <= lhs_contracting_dimensions < rank(lhs).
  • (C7) 0 <= rhs_batching_dimensions < rank(rhs).
  • (C8) 0 <= rhs_contracting_dimensions < rank(rhs).
  • (C9) dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...).
  • (C10) dim(lhs, lhs_contracting_dimensions...) = dim(rhs, rhs_contracting_dimensions...).
  • (C11) size(precision_config) = 2.
  • (C12) shape(result) = dim(lhs, lhs_batching_dimensions) + dim(lhs, lhs_result_dimensions) + dim(rhs, rhs_result_dimensions).
  • Nếu thao tác sử dụng các tensor không được định lượng:
    • (C13) element_type(lhs) = element_type(rhs).
  • Nếu thao tác sử dụng các tensor được định lượng:
    • (C14) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs).
    • (C15) zero_points(rhs) = 0.
    • (C16) Nếu is_per_axis_quantized(rhs), thì quantization_dimension(rhs) không nằm trong rhs_contracting_dimensions.
    • Nếu is_quantized(lhs):
    • (C17) storage_type(lhs) = storage_type(rhs).
    • (C18) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result).
    • (C19) Nếu is_per_tensor_quantized(rhs), thì is_per_tensor_quantized(result).
    • Nếu !is_quantized(lhs):
    • (C20) element_type(lhs) = expressed_type(rhs) = element_type(result).
  • Nếu !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.

Ví dụ

// %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]]
//          ]

 Các ví dụ khác

dynamic_broadcast_in_dim

Ngữ nghĩa

Thao tác này có chức năng giống hệt với thao tác broadcast_in_dim, nhưng hình dạng kết quả được chỉ định linh động thông qua output_dimensions.

Thao tác này cũng chấp nhận các thuộc tính không bắt buộc known_expanding_dimensions, known_nonexpanding_dimensions để thể hiện kiến thức tĩnh về hành vi mở rộng của các phương diện. Nếu bạn không chỉ định, thì tất cả các phương diện đều được giả định là có thể mở rộng.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor được lượng tử hoá (C1-C2), (C5-C6), (C9)
(I2) output_dimensions Tensor 1 chiều thuộc loại số nguyên (C7)
(I3) broadcast_dimensions Tenxơ hằng số 1 chiều thuộc loại số nguyên (C2-C6)
(I4) known_expanding_dimensions Tenxơ hằng số 1 chiều thuộc loại số nguyên (C8-C9)
(I5) known_nonexpanding_dimensions Tenxơ hằng số 1 chiều thuộc loại số nguyên (C8-C9)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá (C1), (C3), (C5-C7)

Giới hạn

  • (C1) element_type(result) được cho bởi:
    • element_type(operand), nếu !is_per_axis_quantized(operand).
    • element_type(operand) ngoại trừ quantization_dimension(operand), scales(operand)zero_points(operand) có thể khác với quantization_dimension(result), scales(result)zero_points(result) tương ứng, nếu không.
  • (C2) size(broadcast_dimensions) = rank(operand).
  • (C3) 0 <= broadcast_dimensions < rank(result).
  • (C4) is_unique(broadcast_dimensions).
  • (C5) Đối với tất cả daxes(operand):
    • dim(operand, d) = 1 hoặc
    • dim(operand, d) = dim(result, broadcast_dimensions[d]).
  • (C6) Nếu is_per_axis_quantized(result):
    • quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)].
    • Nếu dim(operand, quantization_dimension(operand)) = 1, thì 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).

Ví dụ

// %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]
//            ]
//          ]

 Các ví dụ khác

dynamic_conv

Ngữ nghĩa

Thao tác này có chức năng giống hệt với thao tác tích chập, nhưng khoảng đệm được chỉ định một cách linh hoạt thông qua padding.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C1), (C10-C11), (C14) (C25), (C26-C27), (C30-C31), (C33)
(I2) rhs tensor hoặc tensor được lượng tử hoá (C1), (C14-C16), (C26-C28), (C30-C33)
(I3) padding Tensor 2 chiều thuộc loại số nguyên (C4)
(I4) window_strides Hằng số tensor 1 chiều thuộc loại si64 (C2-C3)
(I5) lhs_dilation Hằng số tensor 1 chiều thuộc loại si64 (C5-C6)
(I6) rhs_dilation Hằng số tensor 1 chiều thuộc loại si64 (C7 – C8)
(I7) window_reversal Hằng số tensor 1 chiều thuộc loại i1 (C9)
(I8) input_batch_dimension hằng số thuộc loại si64 (C10), (C13)
(I9) input_feature_dimension hằng số thuộc loại si64 (C11), (C13-C14)
(I10) input_spatial_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C12), (C13)
(I11) kernel_input_feature_dimension hằng số thuộc loại si64 (C14), (C18)
(I12) kernel_output_feature_dimension hằng số thuộc loại si64 (C15-C16), (C18), (C28)
(I13) kernel_spatial_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C17 – C18)
(I14) output_batch_dimension hằng số thuộc loại si64 (C20)
(I15) output_feature_dimension hằng số thuộc loại si64 (C20), (C29)
(I16) output_spatial_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C19 – C20)
(I17) feature_group_count hằng số thuộc loại si64 (C11), (C14), (C16), (C21), (C23)
(I18) batch_group_count hằng số thuộc loại si64 (C10), (C15), (C22), (C23)
(I19) precision_config số lượng enum có thể thay đổi của DEFAULT, HIGHHIGHEST (C24)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá (C25 – C27), (C29), (C31 – C33)

Giới hạn

  • (C1) N = rank(lhs) = rank(rhs).
  • (C2) size(window_strides) = N - 2.
  • (C3) 0 < window_strides.
  • (C4) shape(padding) = [N - 2, 2].
  • (C5) size(lhs_dilation) = N - 2.
  • (C6) 0 < lhs_dilation.
  • (C7) size(rhs_dilation) = N - 2.
  • (C8) 0 < rhs_dilation.
  • (C9) size(window_reversal) = N - 2.
  • (C10) dim(lhs, input_batch_dimension) % batch_group_count = 0.
  • (C11) dim(lhs, input_feature_dimension) % feature_group_count = 0.
  • (C12) size(input_spatial_dimensions) = N - 2.
  • (C13) Cho 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) Điều kiện 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) Cho 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) được xác định là:
    • dim(lhs, input_batch_dimension) / batch_group_count nếu result_dim = output_batch_dimension.
    • dim(rhs, kernel_output_feature_dimension) nếu result_dim = output_feature_dimension.
    • num_windows nếu không, trong đó:
    • 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.
  • Nếu thao tác sử dụng các tensor không được định lượng:
    • (C27) element_type(lhs) = element_type(rhs) = element_type(result).
  • Nếu thao tác sử dụng các tensor được định lượng:
    • (C28) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs).
    • (C29) Nếu is_per_axis_quantized(rhs), thì quantization_dimension(rhs) = kernel_output_feature_dimension.
    • (C30) Nếu is_per_axis_quantized(result), thì quantization_dimension(result) = output_feature_dimension.
    • Nếu is_quantized(lhs):
    • (C31) storage_type(lhs) = storage_type(rhs).
    • (C32) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result).
    • (C33) Nếu is_per_tensor_quantized(rhs), thì is_per_tensor_quantized(result).
    • Nếu !is_quantized(lhs):
    • (C34) element_type(lhs) = expressed_type(rhs) = element_type(result).

Ví dụ

// %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]]
//          ]]

 Các ví dụ khác

dynamic_gather

Ngữ nghĩa

Thao tác này có chức năng giống hệt như thao tác gather, trong đó slice_sizes được chỉ định một cách linh động dưới dạng một giá trị.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C7), (C10-C12), (C14)
(I2) start_indices tensor thuộc loại số nguyên (C2), (C3), (C13)
(I3) slice_sizes Tensor 1 chiều thuộc loại số nguyên (C8), (C11-C13)
(I4) offset_dims Hằng số tensor 1 chiều thuộc loại si64 (C1), (C4-C5), (C13)
(I5) collapsed_slice_dims Hằng số tensor 1 chiều thuộc loại si64 (C1), (C6-C8), (C13)
(I6) start_index_map Hằng số tensor 1 chiều thuộc loại si64 (C3), (C9), (C10)
(I7) index_vector_dim hằng số thuộc loại si64 (C2), (C3), (C13)
(I8) indices_are_sorted hằng số thuộc loại i1

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C5), (C13-C14)

Giới hạn

  • (C1) rank(operand) = size(offset_dims) + size(collapsed_slice_dims).
  • (C2) 0 <= index_vector_dim <= rank(start_indices).
  • (C3) size(start_index_map) = index_vector_dim < rank(start_indices) ? dim(start_indices, index_vector_dim) : 1.
  • (C4) is_unique(offset_dims) and is_sorted(offset_dims).
  • (C5) 0 <= offset_dims < rank(result).
  • (C6) is_unique(collapsed_slice_dims) and is_sorted(collapsed_slice_dims).
  • (C7) 0 <= collapsed_slice_dims < rank(operand).
  • (C8) slice_sizes[collapsed_slice_dims...] <= 1.
  • (C9) is_unique(start_index_map).
  • (C10) 0 <= start_index_map < rank(operand).
  • (C11) size(slice_sizes) = rank(operand).
  • (C12) 0 <= slice_sizes <= shape(operand).
  • (C13) shape(result) = combine(batch_dim_sizes, offset_dim_sizes) trong đó:
    • batch_dim_sizes = shape(start_indices), ngoại trừ kích thước phương diện start_indices tương ứng với index_vector_dim không được đưa vào.
    • offset_dim_sizes = shape(slice_sizes), ngoại trừ kích thước phương diện trong slice_sizes tương ứng với collapsed_slice_dims không được đưa vào.
    • combine đặt batch_dim_sizes tại các trục tương ứng với batch_dimsoffset_dim_sizes tại các trục tương ứng với offset_dims.
  • (C14) element_type(operand) = element_type(result).

Ví dụ

// %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]]
//            ]
//          ]

 Các ví dụ khác

dynamic_iota

Ngữ nghĩa

Thao tác này có chức năng giống hệt với iota op, nhưng hình dạng kết quả được chỉ định linh động thông qua output_shape.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) output_shape Tensor 1 chiều thuộc loại số nguyên (C1), (C2)
(I2) iota_dimension si64 (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C2)

Giới hạn

  • (C1) 0 <= iota_dimension < size(output_shape).
  • (C2) rank(result) = size(output_shape).

Ví dụ

%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]
//          ]

 Các ví dụ khác

dynamic_pad

Ngữ nghĩa

Thao tác này có chức năng giống hệt với thao tác pad, nhưng edge_padding_low, edge_padding_highinterior_padding được chỉ định linh động dưới dạng các giá trị.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C2), (C4)
(I2) padding_value Tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor (C1)
(I3) edge_padding_low Tensor 1 chiều thuộc loại số nguyên (C1), (C4)
(I4) edge_padding_high Tensor 1 chiều thuộc loại số nguyên (C1), (C4)
(I5) interior_padding Tensor 1 chiều thuộc loại số nguyên (C2-C4)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá theo từng tensor (C3 – C6)

Giới hạn

  • (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.

Ví dụ

// %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]
//          ]

 Các ví dụ khác

dynamic_reshape

Ngữ nghĩa

Thao tác này có chức năng giống hệt với thao tác reshape, nhưng hình dạng kết quả được chỉ định linh động thông qua output_shape.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor được lượng tử hoá (C1-C3)
(I2) output_shape Tensor 1 chiều thuộc loại số nguyên (C4)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá (C1-C4)

Giới hạn

  • (C1) element_type(result) được cho bởi:
    • element_type(operand), nếu !is_per_axis_quantized(operand).
    • element_type(operand), ngoại trừ quantization_dimension(operand)quantization_dimension(result) có thể khác nhau.
  • (C2) size(operand) = size(result).
  • (C3) Nếu 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).

Ví dụ

// %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]]

 Các ví dụ khác

dynamic_slice

Ngữ nghĩa

Trích xuất một lát cắt từ operand bằng cách sử dụng các chỉ mục bắt đầu được tính toán động và tạo ra một tensor result. start_indices chứa chỉ mục bắt đầu của lát cho mỗi phương diện, có thể điều chỉnh và slice_sizes chứa kích thước của lát cho mỗi phương diện. Một cách chính thức hơn, result[result_index] = operand[operand_index] trong đó:

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

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C2), (C4)
(I2) start_indices số lượng biến đổi của tensor 0 chiều thuộc loại số nguyên (C2), (C3)
(I3) slice_sizes Hằng số tensor 1 chiều thuộc loại si64 (C2), (C4), (C5)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C5)

Giới hạn

  • (C1) element_type(operand) = element_type(result).
  • (C2) size(start_indices) = size(slice_sizes) = rank(operand).
  • (C3) same(type(start_indices...)).
  • (C4) 0 <= slice_sizes <= shape(operand).
  • (C5) shape(result) = slice_sizes.

Ví dụ

// %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]
//          ]

 Các ví dụ khác

dynamic_update_slice

Ngữ nghĩa

Tạo một tensor result bằng với tensor operand, ngoại trừ việc lát bắt đầu từ start_indices được cập nhật bằng các giá trị trong update. Cụ thể hơn, result[result_index] được xác định là:

  • update[update_index] nếu 0 <= update_index < shape(update) trong đó:
    • adjusted_start_indices = clamp(0, start_indices, shape(operand) - shape(update)).
    • update_index = result_index - adjusted_start_indices.
  • Nếu không thì là operand[result_index].

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1-C4), (C6)
(I2) update tensor hoặc tensor lượng tử hoá trên mỗi tensor (C2), (C3), (C6)
(I3) start_indices số lượng biến đổi của tensor 0 chiều thuộc loại số nguyên (C4), (C5)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]
//          ]

 Các ví dụ khác

hàm mũ

Ngữ nghĩa

Thực hiện phép toán hàm mũ trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: exp từ IEEE-754.
  • Đối với số phức: hàm mũ phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(exponential, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

exponential_minus_one

Ngữ nghĩa

Thực hiện phép toán trừ 1 theo hàm mũ trên tensor operand và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: expm1 từ IEEE-754.
  • Đối với số phức: số mũ phức trừ đi 1.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(exponential_minus_one, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

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

 Các ví dụ khác

fft

Ngữ nghĩa

Thực hiện phép biến đổi Fourier thuận và nghịch cho các đầu vào/đầu ra thực và phức.

fft_type là một trong những giá trị sau:

  • FFT: Chuyển tiếp FFT phức tạp sang phức tạp.
  • IFFT: FFT phức tạp sang phức tạp nghịch đảo.
  • RFFT: Chuyển tiếp FFT thực sang phức.
  • IRFFT: FFT thực sang phức nghịch đảo (tức là lấy số phức, trả về số thực).

Cụ thể hơn, cho hàm fft nhận các tensor 1 chiều thuộc các kiểu phức tạp làm dữ liệu đầu vào, tạo ra các tensor 1 chiều thuộc cùng các kiểu làm dữ liệu đầu ra và tính toán phép biến đổi Fourier rời rạc:

Đối với fft_type = FFT, result được xác định là kết quả cuối cùng của một loạt các phép tính L, trong đó L = size(fft_length). Ví dụ: đối với 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]).

Hơn nữa, cho hàm ifft có cùng chữ ký kiểu và tính toán nghịch đảo của fft:

Đối với fft_type = IFFT, result được xác định là nghịch đảo của các phép tính cho fft_type = FFT. Ví dụ: đối với 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, ..., :]).

Ngoài ra, cho hàm rfft lấy tensor 1 chiều thuộc các loại dấu phẩy động, tạo ra tensor 1 chiều thuộc các loại phức tạp có cùng ngữ nghĩa dấu phẩy động và hoạt động như sau:

  • rfft(real_operand) = truncated_result nơi
  • complex_operand... = (real_operand..., 0.0).
  • complex_result = fft(complex_operand).
  • truncated_result = complex_result[:(rank(complex_result) / 2 + 1)].

(Khi biến đổi Fourier rời rạc được tính cho các toán hạng thực, N/2 + 1 phần tử đầu tiên của kết quả xác định rõ ràng phần còn lại của kết quả, vì vậy kết quả của rfft sẽ bị cắt bớt để tránh tính toán các phần tử dư thừa).

Đối với fft_type = RFFT, result được xác định là kết quả cuối cùng của một loạt các phép tính L, trong đó L = size(fft_length). Ví dụ: đối với 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]).

Cuối cùng, cho hàm irfft có cùng chữ ký kiểu và tính toán nghịch đảo của rfft:

Đối với fft_type = IRFFT, result được xác định là nghịch đảo của các phép tính cho fft_type = RFFT. Ví dụ: đối với 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, ..., :]).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại số phức hoặc dấu phẩy động (C1), (C2), (C4), (C5)
(I2) fft_type enum của FFT, IFFT, RFFTIRFFT (C2), (C5)
(I3) fft_length Hằng số tensor 1 chiều thuộc loại si64 (C1), (C3), (C4)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số phức hoặc dấu phẩy động (C2), (C4), (C5)

Giới hạn

  • (C1) size(fft_length) <= rank(operand).
  • (C2) Mối quan hệ giữa các loại phần tử operandresult có sự khác biệt:
    • Nếu fft_type = FFT, element_type(operand)element_type(result) có cùng kiểu phức tạp.
    • Nếu fft_type = IFFT, element_type(operand)element_type(result) có cùng kiểu phức tạp.
    • Nếu fft_type = RFFT, element_type(operand) là một kiểu dấu phẩy động và element_type(result) là một kiểu phức tạp có cùng ngữ nghĩa dấu phẩy động.
    • Nếu fft_type = IRFFT, element_type(operand) là một kiểu phức tạp và element_type(result) là một kiểu dấu phẩy động có cùng ngữ nghĩa dấu phẩy động.
  • (C3) 1 <= size(fft_length) <= 3.
  • (C4) Nếu trong số operandresult, có một tensor real thuộc loại dấu phẩy động, thì shape(real)[-size(fft_length):] = fft_length.
  • (C5) shape(result) = shape(operand) ngoại trừ:
    • Nếu fft_type = RFFT, dim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1.
    • Nếu fft_type = IRFFT, dim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1.

Ví dụ

// %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)]

tầng

Ngữ nghĩa

Thực hiện phép làm tròn xuống theo phần tử của tensor operand và tạo ra một tensor result. Triển khai thao tác roundToIntegralTowardNegative theo quy cách IEEE-754. Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(floor, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

tập hợp

Ngữ nghĩa

Thu thập các lát cắt từ tensor operand từ các độ lệch được chỉ định trong start_indices và tạo ra một tensor result.

Sơ đồ sau đây cho thấy cách các phần tử trong result liên kết với các phần tử trong operand bằng một ví dụ cụ thể. Sơ đồ này chọn một vài chỉ mục result mẫu và giải thích chi tiết chỉ mục operand mà chúng tương ứng.

tập hợp

Một cách chính thức hơn, result[result_index] = operand[operand_index] trong đó:

  • batch_dims = [d for d in axes(result) and d not in offset_dims].
  • batch_index = result_index[batch_dims...].
  • start_index được xác định là:
    • start_indices[bi0, ..., :, ..., biN] trong đó bi là các phần tử riêng lẻ trong batch_index: được chèn vào chỉ mục index_vector_dim, nếu index_vector_dim < rank(start_indices).
    • Nếu không thì là [start_indices[batch_index]].
  • Đối với d_operand trong axes(operand),
    • full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand]) nếu d_operand = start_index_map[d_start].
    • Nếu không thì là full_start_index[d_operand] = 0.
  • Đối với d_operand trong axes(operand),
    • full_batching_index[d_operand] = batch_index[d_start - (d_start < index_vector_dim ? 0 : 1)] nếu d_operand = operand_batching_dims[i_batching]d_start = start_indices_batching_dims[i_batching].
    • Nếu không thì là full_batching_index[d_operand] = 0.
  • offset_index = result_index[offset_dims...].
  • full_offset_index = [oi0, ..., 0, ..., oiN] trong đó oi là các phần tử riêng lẻ trong offset_index0 được chèn ở các chỉ mục từ collapsed_slice_dimsoperand_batching_dims.
  • operand_index = full_start_index + full_batching_index + full_offset_index.

Nếu indices_are_sortedtrue thì quá trình triển khai có thể giả định rằng start_indices được sắp xếp theo start_index_map, nếu không, hành vi sẽ không xác định. Cụ thể hơn, đối với tất cả i1 < i2 từ indices(result), full_start_index(i1) <= full_start_index(i2).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C8), (C11), (C17), (C19-C21), (C23)
(I2) start_indices tensor thuộc loại số nguyên (C2-C3), (C14), (C17), (C22)
(I3) offset_dims Hằng số tensor 1 chiều thuộc loại si64 (C1), (C4-C5), (C22)
(I4) collapsed_slice_dims Hằng số tensor 1 chiều thuộc loại si64 (C1), (C6-C9), (C22)
(I5) operand_batching_dims Hằng số tensor 1 chiều thuộc loại si64 (C1), (C6), (C10-C12), (C16-C18), (C22)
(I6) start_indices_batching_dims Hằng số tensor 1 chiều thuộc loại si64 (C13 – C17)
(I7) start_index_map Hằng số tensor 1 chiều thuộc loại si64 (C3), (C18 – C19)
(I8) index_vector_dim hằng số thuộc loại si64 (C2-C3), (C15), (C22)
(I9) slice_sizes Hằng số tensor 1 chiều thuộc loại si64 (C9), (C12), (C20-C22)
(I10) indices_are_sorted hằng số thuộc loại i1

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C5), (C22-C23)

Giới hạn

  • (C1) rank(operand) = size(offset_dims) + size(collapsed_slice_dims) + size(operand_batching_dims).
  • (C2) 0 <= index_vector_dim <= rank(start_indices).
  • (C3) size(start_index_map) = index_vector_dim < rank(start_indices) ? dim(start_indices, index_vector_dim) : 1.
  • (C4) is_unique(offset_dims) and is_sorted(offset_dims).
  • (C5) 0 <= offset_dims < rank(result).
  • (C6) is_unique(concatenate(collapsed_slice_dims, operand_batching_dims))
  • (C7) is_sorted(collapsed_slice_dims).
  • (C8) 0 <= collapsed_slice_dims < rank(operand).
  • (C9) slice_sizes[collapsed_slice_dims...] <= 1.
  • (C10) is_sorted(operand_batching_dims).
  • (C11) 0 <= operand_batching_dims < rank(operand).
  • (C12) slice_sizes[operand_batching_dims...] <= 1.
  • (C13) is_unique(start_indices_batching_dims).
  • (C14) 0 <= start_indices_batching_dims < rank(start_indices).
  • (C15) index_vector_dim not in start_indices_batching_dims.
  • (C16) size(operand_batching_dims) == size(start_indices_batching_dims).
  • (C17) dim(operand, operand_batching_dims...) = dim(start_indices, start_indices_batching_dims...).
  • (C18) is_unique(concatenate(start_index_map, operand_batching_dims)).
  • (C19) 0 <= start_index_map < rank(operand).
  • (C20) size(slice_sizes) = rank(operand).
  • (C21) 0 <= slice_sizes <= shape(operand).
  • (C22) shape(result) = combine(batch_dim_sizes, offset_dim_sizes) trong đó:
    • batch_dim_sizes = shape(start_indices), ngoại trừ kích thước phương diện start_indices tương ứng với index_vector_dim không được đưa vào.
    • offset_dim_sizes = slice_sizes, ngoại trừ kích thước phương diện trong slice_sizes tương ứng với collapsed_slice_dimsoperand_batching_dims không được đưa vào.
    • combine đặt batch_dim_sizes tại các trục tương ứng với batch_dimsoffset_dim_sizes tại các trục tương ứng với offset_dims.
  • (C23) element_type(operand) = element_type(result).

Ví dụ

// %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]]
//            ]
//           ]
//          ]

 Các ví dụ khác

get_dimension_size

Ngữ nghĩa

Tạo ra kích thước của dimension đã cho của operand. Nói một cách trang trọng hơn, result = dim(operand, dimension). Ngữ nghĩa chỉ liên quan đến thành phần hình dạng của loại. Loại phần tử có thể là bất kỳ loại nào.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor được lượng tử hoá (C1)
(I2) dimension hằng số thuộc loại si64 (C1)

Kết quả đầu ra

Tên Loại
result Tensor 0 chiều thuộc loại si32

Giới hạn

  • (C1) 0 <= dimension < rank(operand).

Ví dụ

// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.get_dimension_size"(%operand) {
  dimension = 1 : i64
} : (tensor<2x3xi64>) -> tensor<i32>
// %result: 3

 Các ví dụ khác

get_tuple_element

Ngữ nghĩa

Trích xuất phần tử ở vị trí index của bộ operand và tạo ra một result. Nói một cách trang trọng hơn, result = operand[index].

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tuple (C1), (C2)
(I2) index hằng số thuộc loại si32 (C1), (C2)

Kết quả đầu ra

Tên Loại Giới hạn
result giá trị bất kỳ (C2)

Giới hạn

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

Ví dụ

// %operand: ([1.0, 2.0], (3))
%result = "stablehlo.get_tuple_element"(%operand) <{index = 0 : i32}> : (tuple<tensor<2xf64>, tuple<tensor<i64>>>) -> tensor<2xf64>
// %result: [1.0, 2.0]

 Các ví dụ khác

nếu

Ngữ nghĩa

Tạo ra kết quả đầu ra từ việc thực thi chính xác một hàm từ true_branch hoặc false_branch tuỳ thuộc vào giá trị của pred. Nói một cách trang trọng hơn, result = pred ? true_branch() : false_branch().

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) pred Tensor 0 chiều thuộc loại i1
(I2) true_branch hàm (C1-C3)
(I3) false_branch hàm (C1), (C2)

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor, tensor lượng tử hoá hoặc mã thông báo có thể thay đổi (C3)

Giới hạn

  • (C1) input_types(true_branch) = input_types(false_branch) = [].
  • (C2) output_types(true_branch) = output_types(false_branch).
  • (C3) type(results...) = output_types(true_branch).

Ví dụ

// %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

 Các ví dụ khác

imag

Ngữ nghĩa

Trích xuất phần ảo theo phần tử từ operand và tạo ra một tensor result. Cụ thể hơn, đối với mỗi phần tử x: imag(x) = is_complex(x) ? imaginary_part(x) : constant(0, element_type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại số phức hoặc dấu phẩy động (C1), (C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động (C1), (C2)

Giới hạn

  • (C1) shape(result) = shape(operand).
  • (C2) element_type(result) được xác định là:
    • complex_element_type(element_type(operand)) nếu is_complex(operand).
    • Nếu không thì là element_type(operand).

Ví dụ

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

 Các ví dụ khác

trong nguồn cấp dữ liệu

Ngữ nghĩa

Đọc dữ liệu từ nguồn cấp dữ liệu trong luồng và tạo ra results.

Ngữ nghĩa của infeed_config được xác định theo cách triển khai.

results bao gồm các giá trị tải trọng xuất hiện trước và mã thông báo xuất hiện sau cùng. Trong tương lai, chúng tôi dự định chia tải trọng và mã thông báo thành hai đầu ra riêng biệt để cải thiện độ rõ ràng (#670).

Thông tin đầu vào

Hãng nhạc Tên Loại
(I1) token token
(I2) infeed_config hằng số thuộc loại string

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor, tensor lượng tử hoá hoặc mã thông báo có thể thay đổi (C1-C3)

Giới hạn

  • (C1) 0 < size(results).
  • (C2) is_empty(result[:-1]) hoặc is_tensor(type(results[:-1])).
  • (C3) is_token(type(results[-1])).

Ví dụ

// %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]]

 Các ví dụ khác

iota

Ngữ nghĩa

Điền vào một tenxơ output các giá trị theo thứ tự tăng dần bắt đầu từ 0 dọc theo phương diện iota_dimension. Nói một cách trang trọng hơn,

output[output_index] = constant(is_quantized(output) ? quantize(output_index[iota_dimension], element_type(output)) : output_index[iota_dimension], element_type(output)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) iota_dimension si64 (C1)

Kết quả đầu ra

Tên Loại Giới hạn
output tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

  • (C1) 0 <= iota_dimension < rank(output).

Ví dụ

%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]
//          ]

 Các ví dụ khác

is_finite

Ngữ nghĩa

Thực hiện kiểm tra từng phần tử xem giá trị trong x có hữu hạn hay không (tức là không phải +Inf, -Inf hoặc NaN) và tạo ra một tensor y. Triển khai thao tác isFinite theo quy cách IEEE-754. Đối với các loại được phân lượng hoá, kết quả luôn là true.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) x tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
y tensor thuộc loại boolean (C1)

Giới hạn

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

Ví dụ

// 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]

 Các ví dụ khác

log

Ngữ nghĩa

Thực hiện phép toán lôgarit trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: log từ IEEE-754.
  • Đối với số phức: lôgarit phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(log, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

log_plus_one

Ngữ nghĩa

Thực hiện phép toán lôgarit cộng một trên tensor operand và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: logp1 từ IEEE-754.
  • Đối với số phức: complex(log(hypot(real(x) + 1, imag(x))), atan2(imag(x), real(x) + 1))
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(log_plus_one, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

logistic

Ngữ nghĩa

Thực hiện phép toán logistic theo phần tử trên tensor operand và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: division(1, addition(1, exp(-x))) từ IEEE-754.
  • Đối với số phức: hàm logistic phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(logistic, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

map

Ngữ nghĩa

Áp dụng hàm ánh xạ computation cho inputs dọc theo dimensions và tạo ra một tensor result.

Nói một cách trang trọng hơn, result[result_index] = computation(inputs...[result_index]).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) inputs số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C1-C4)
(I2) dimensions Hằng số tensor 1 chiều thuộc loại si64 (C3)
(I3) computation hàm (C4)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá theo từng tensor (C1), (C4)

Giới hạn

  • (C1) shape(inputs...) = shape(result).
  • (C2) 0 < size(inputs) = N.
  • (C3) dimensions = range(rank(inputs[0])).
  • (C4) computation có loại (tensor<E0>, ..., tensor<EN-1>) -> tensor<E'>, trong đó Ei = element_type(inputs[i])E' = element_type(result).

Ví dụ

// %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]]

 Các ví dụ khác

tối đa

Ngữ nghĩa

Thực hiện phép toán max theo phần tử trên các tensor lhsrhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với giá trị boolean: OR logic.
  • Đối với số nguyên: số nguyên tối đa.
  • Đối với số thực: maximum từ IEEE-754.
  • Đối với số phức: giá trị tối đa theo thứ tự từ điển cho cặp (real, imaginary). Việc áp đặt một thứ tự cho các số phức liên quan đến ngữ nghĩa đáng ngạc nhiên, vì vậy trong tương lai, chúng tôi dự định xoá hỗ trợ cho các số phức đối với thao tác này (#560).
  • Đối với các loại được lượng tử hoá:
    • dequantize_op_quantize(maximum, lhs, rhs, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C1)
(I2) rhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

tối thiểu

Ngữ nghĩa

Thực hiện phép toán min theo phần tử trên các tensor lhsrhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với giá trị boolean: phép toán AND logic.
  • Đối với số nguyên: số nguyên tối thiểu.
  • Đối với số thực: minimum từ IEEE-754.
  • Đối với số phức: giá trị tối thiểu theo thứ tự từ điển cho cặp (real, imaginary). Việc áp đặt một thứ tự cho các số phức liên quan đến ngữ nghĩa đáng ngạc nhiên, vì vậy trong tương lai, chúng tôi dự định xoá hỗ trợ cho các số phức đối với thao tác này (#560).
  • Đối với các loại được lượng tử hoá:
    • dequantize_op_quantize(minimum, lhs, rhs, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C1)
(I2) rhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

nhân

Ngữ nghĩa

Thực hiện phép nhân từng phần tử của hai tensor lhsrhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với giá trị boolean: phép toán AND logic.
  • Đối với số nguyên: phép nhân số nguyên.
  • Đối với số thực: multiplication từ IEEE-754.
  • Đối với số phức: phép nhân số phức.
  • Đối với các loại được lượng tử hoá:
    • dequantize_op_quantize(multiply, lhs, rhs, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C1)
(I2) rhs tensor hoặc tensor được lượng tử hoá theo từng tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

phủ định

Ngữ nghĩa

Thực hiện phép phủ định theo phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số nguyên có dấu: phủ định số nguyên.
  • Đối với số nguyên không dấu: bitcast thành số nguyên có dấu, phủ định số nguyên, bitcast trở lại số nguyên không dấu.
  • Đối với số thực: negate từ IEEE-754.
  • Đối với số phức: phủ định số phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(negate, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// 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]

 Các ví dụ khác

không

Ngữ nghĩa

Thực hiện phép toán NOT theo phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với giá trị boolean: phủ định trong logic.
  • Đối với số nguyên: bitwise NOT.

Đối số

Tên Loại Giới hạn
operand tensor thuộc loại boolean hoặc số nguyên (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại boolean hoặc số nguyên (C1)

Giới hạn

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

Ví dụ

// 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]

 Các ví dụ khác

optimization_barrier

Ngữ nghĩa

Đảm bảo rằng các thao tác tạo ra operand được thực thi trước mọi thao tác phụ thuộc vào result và ngăn các hoạt động chuyển đổi của trình biên dịch di chuyển các thao tác qua rào cản. Ngoài ra, thao tác này là một danh tính, tức là result = operand.

Đối số

Tên Loại Giới hạn
operand số lượng tensor thay đổi, tensor hoặc mã thông báo được lượng tử hoá theo từng tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result số lượng tensor thay đổi, tensor hoặc mã thông báo được lượng tử hoá theo từng tensor (C1)

Giới hạn

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

Ví dụ

// %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

 Các ví dụ khác

hoặc

Ngữ nghĩa

Thực hiện phép OR theo phần tử của hai tensor lhsrhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với giá trị boolean: OR logic.
  • Đối với số nguyên: đảo bit OR.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại số nguyên hoặc boolean (C1)
(I2) rhs tensor thuộc loại số nguyên hoặc boolean (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên hoặc boolean (C1)

Giới hạn

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

Ví dụ

// 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]]

 Các ví dụ khác

outfeed

Ngữ nghĩa

Ghi inputs vào nguồn cấp dữ liệu bên ngoài và tạo mã thông báo result.

Ngữ nghĩa của outfeed_config được xác định theo cách triển khai.

Thông tin đầu vào

Hãng nhạc Tên Loại
(I1) inputs số lượng tensor hoặc tensor lượng tử hoá có thể thay đổi
(I2) token token
(I3) outfeed_config hằng số thuộc loại string

Kết quả đầu ra

Tên Loại
result token

Ví dụ

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

 Các ví dụ khác

miếng đệm

Ngữ nghĩa

Mở rộng operand bằng cách thêm khoảng đệm xung quanh tensor cũng như giữa các phần tử của tensor bằng padding_value đã cho.

edge_padding_lowedge_padding_high lần lượt chỉ định lượng khoảng đệm được thêm vào ở đầu dưới (bên cạnh chỉ mục 0) và đầu trên (bên cạnh chỉ mục cao nhất) của mỗi phương diện. Lượng khoảng đệm có thể là số âm, trong đó giá trị tuyệt đối của khoảng đệm âm cho biết số lượng phần tử cần xoá khỏi phương diện được chỉ định.

interior_padding chỉ định lượng khoảng đệm được thêm giữa hai phần tử bất kỳ trong mỗi phương diện (không được âm). Khoảng đệm bên trong xuất hiện trước khoảng đệm cạnh để khoảng đệm cạnh âm sẽ xoá các phần tử khỏi toán hạng có khoảng đệm bên trong.

Cụ thể hơn, result[result_index] được xác định là:

  • operand[operand_index] nếu result_index = edge_padding_low + operand_index * (interior_padding + 1).
  • Nếu không thì là padding_value.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C2), (C4)
(I2) padding_value Tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor (C1)
(I3) edge_padding_low Hằng số tensor 1 chiều thuộc loại si64 (C1), (C4)
(I4) edge_padding_high Hằng số tensor 1 chiều thuộc loại si64 (C1), (C4)
(I5) interior_padding Hằng số tensor 1 chiều thuộc loại si64 (C2-C4)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá theo từng tensor (C3 – C6)

Giới hạn

  • (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.

Ví dụ

// %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]
//          ]

 Các ví dụ khác

partition_id

Ngữ nghĩa

Tạo partition_id của quy trình hiện tại.

Kết quả đầu ra

Tên Loại
result Tensor 0 chiều thuộc loại ui32

Ví dụ

%result = "stablehlo.partition_id"() : () -> tensor<ui32>

 Các ví dụ khác

popcnt

Ngữ nghĩa

Thực hiện việc đếm theo phần tử số lượng bit được đặt trong tenxơ operand và tạo ra một tenxơ result.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại số nguyên (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên (C1)

Giới hạn

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

Ví dụ

// %operand: [0, 1, 2, 127]
%result = "stablehlo.popcnt"(%operand) : (tensor<4xi64>) -> tensor<4xi64>
// %result: [0, 1, 1, 7]

 Các ví dụ khác

công suất

Ngữ nghĩa

Thực hiện phép luỹ thừa theo phần tử của tensor lhs bằng tensor rhs và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số nguyên: luỹ thừa số nguyên.
  • Đối với số thực: pow từ IEEE-754.
  • Đối với số phức: hàm phân phối mũ phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(power, lhs, rhs, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)
(I2) rhs tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

thực

Ngữ nghĩa

Trích xuất phần thực, theo từng phần tử, từ operand và tạo ra một tensor result. Cụ thể hơn, đối với mỗi phần tử x: real(x) = is_complex(x) ? real_part(x) : x.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại số phức hoặc dấu phẩy động (C1), (C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động (C1), (C2)

Giới hạn

  • (C1) shape(result) = shape(operand).
  • (C2) element_type(result) được xác định là:
    • complex_element_type(element_type(operand)) nếu is_complex(operand).
    • Nếu không thì là element_type(operand).

Ví dụ

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

 Các ví dụ khác

recv

Ngữ nghĩa

Nhận dữ liệu từ một kênh có channel_id và tạo ra results.

Nếu is_host_transfertrue, thì thao tác này sẽ chuyển dữ liệu từ máy chủ. Nếu không, dữ liệu sẽ được chuyển từ một thiết bị khác dựa trên các giá trị của source_target_pairs. Cờ này trùng lặp thông tin được cung cấp trong channel_type, vì vậy, trong tương lai, chúng tôi dự định chỉ giữ lại một trong hai cờ này (#666). Nếu is_host_transfer = falsesource_target_pairsNone hoặc trống, thì đó được coi là hành vi không xác định.

results bao gồm các giá trị tải trọng xuất hiện trước và mã thông báo xuất hiện sau cùng. Trong tương lai, chúng tôi dự định chia tải trọng và mã thông báo thành hai đầu ra riêng biệt để cải thiện độ rõ ràng (#670).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) token token
(I2) channel_id hằng số thuộc loại si64
(I3) channel_type enum của DEVICE_TO_DEVICEDEVICE_TO_HOST (C5)
(I4) is_host_transfer hằng số thuộc loại i1 (C5-C6)
(I5) source_target_pairs Hằng số tensor 2 chiều thuộc loại si64 (C1-C4), (C6)

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor, tensor lượng tử hoá hoặc mã thông báo có thể thay đổi (C2-C4)

Giới hạn

  • (C1) dim(source_target_pairs, 1) = 2.
  • (C2) is_unique(source_target_pairs[:, 0]).
  • (C3) is_unique(source_target_pairs[:, 1]).
  • (C4) 0 <= source_target_pairs < N, trong đó N được xác định là:
    • num_replicas nếu bạn sử dụng cross_replica.
    • num_partitions nếu bạn sử dụng cross_partition.
  • (C5) channel_type được xác định là:
    • DEVICE_TO_HOST nếu is_host_transfer = true,
    • Nếu không thì là DEVICE_TO_DEVICE.

Ví dụ

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

 Các ví dụ khác

giảm

Ngữ nghĩa

Áp dụng hàm rút gọn body cho inputsinit_values dọc theo dimensions và tạo ra các tensor results.

Thứ tự giảm được xác định theo cách triển khai, tức là bodyinit_values phải tạo thành một đơn nguyên để đảm bảo rằng thao tác này tạo ra cùng một kết quả cho tất cả các đầu vào trên tất cả các cách triển khai. Tuy nhiên, điều kiện này không áp dụng cho nhiều phép quy giản phổ biến. Ví dụ: phép cộng dấu phẩy động cho body và 0 cho init_values thực sự không tạo thành một đơn thể vì phép cộng dấu phẩy động không có tính kết hợp.

Một cách chính thức hơn, results...[j0, ..., jR-1] = reduce(input_slices_converted) trong đó:

  • input_slices = inputs...[j0, ..., :, ..., jR-1], trong đó : được chèn vào 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) cho một số cây nhị phân schedule trong đó:
    • exec(node) = body(exec(node.left), exec(node.right)).
    • exec(leaf) = leaf.value.
  • schedule là một cây nhị phân đầy đủ do quá trình triển khai xác định, có thứ tự duyệt theo thứ tự bao gồm:
    • Giá trị input_slices_converted...[index] cho tất cả index trong index_space(input_slices_converted) theo thứ tự từ điển tăng dần của index.
    • Được xen kẽ với một lượng init_values_converted do quá trình triển khai xác định ở các vị trí do quá trình triển khai xác định.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) inputs số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C1-C4), (C6), (C7)
(I2) init_values số lượng biến đổi của tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor (C2), (C3)
(I3) dimensions Hằng số tensor 1 chiều thuộc loại si64 (C4), (C5), (C7)
(I4) body hàm (C6)

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C3), (C7), (C8)

Giới hạn

  • (C1) same(shape(inputs...)).
  • (C2) element_type(inputs...) = element_type(init_values...).
  • (C3) 0 < size(inputs) = size(init_values) = size(results) = N.
  • (C4) 0 <= dimensions < rank(inputs[0]).
  • (C5) is_unique(dimensions).
  • (C6) body có kiểu (tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>) trong đó is_promotable(element_type(inputs[i]), Ei).
  • (C7) shape(results...) = shape(inputs...), ngoại trừ kích thước phương diện của inputs... tương ứng với dimensions không được đưa vào.
  • (C8) element_type(results[i]) = Ei cho tất cả i trong [0,N).

Ví dụ

// %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]

 Các ví dụ khác

reduce_precision

Ngữ nghĩa

Thực hiện chuyển đổi từng phần tử của operand sang một loại dấu phẩy động khác sử dụng exponent_bitsmantissa_bits rồi quay lại loại dấu phẩy động ban đầu và tạo ra một tensor output.

Một cách trang trọng hơn:

  • Các bit phần định trị của giá trị ban đầu được cập nhật để làm tròn giá trị ban đầu thành giá trị gần nhất có thể biểu thị bằng mantissa_bits bằng cách sử dụng ngữ nghĩa roundToIntegralTiesToEven.
  • Sau đó, nếu mantissa_bits nhỏ hơn số lượng bit phần định trị của giá trị ban đầu, thì các bit phần định trị sẽ bị cắt bớt thành mantissa_bits.
  • Sau đó, nếu các bit số mũ của kết quả trung gian không nằm trong phạm vi do exponent_bits cung cấp, thì kết quả trung gian sẽ tràn sang vô cùng bằng cách sử dụng dấu ban đầu hoặc tràn xuống 0 bằng cách sử dụng dấu ban đầu.
  • Đối với các loại được lượng tử hoá, hãy thực hiện dequantize_op_quantize( lambda operand: reduce_precision(operand, exponent_bits, mantissa_bits), operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)
(I2) exponent_bits hằng số thuộc loại si32 (C2)
(I3) mantissa_bits hằng số thuộc loại si32 (C3)

Kết quả đầu ra

Tên Loại Giới hạn
output tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// 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]

 Các ví dụ khác

reduce_scatter

Ngữ nghĩa

reduce_scatter

Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy thực hiện thao tác giảm bằng cách sử dụng computations trên các giá trị của tensor operand từ mỗi quy trình, chia kết quả giảm dọc theo scatter_dimension thành các phần và phân tán các phần đã chia giữa các quy trình để tạo ra result.

Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:

  • cross_replica(replica_groups) nếu channel_id <= 0 and use_global_device_ids = false.
  • cross_replica_and_partition(replica_groups) nếu channel_id > 0 and use_global_device_ids = false.
  • flattened_ids(replica_groups) nếu channel_id > 0 and use_global_device_ids = true.

Sau đó, trong mỗi 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] cho tất cả sender trong process_group, trong đó receiver_index = process_group.index(receiver).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C2), (C7), (C8)
(I2) scatter_dimension hằng số thuộc loại si64 (C1), (C2), (C8)
(I3) replica_groups Hằng số tensor 2 chiều thuộc loại si64 (C3 – C5)
(I4) channel_id hằng số thuộc loại si64 (C6)
(I5) use_global_device_ids hằng số thuộc loại i1 (C6)
(I6) computation hàm (C7)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C8-C9)

Giới hạn

  • (C1) dim(operand, scatter_dimension) % dim(process_groups, 1) = 0.
  • (C2) 0 <= scatter_dimension < rank(operand).
  • (C3) is_unique(replica_groups).
  • (C4) size(replica_groups) được xác định là:
    • num_replicas nếu bạn sử dụng cross_replica.
    • num_replicas nếu bạn sử dụng cross_replica_and_partition.
    • num_processes nếu bạn sử dụng flattened_ids.
  • (C5) 0 <= replica_groups < size(replica_groups).
  • (C6) Nếu use_global_device_ids = true, thì channel_id > 0.
  • (C7) computation có loại (tensor<E>, tensor<E>) -> (tensor<E>) trong đó is_promotable(element_type(operand), E).
  • (C8) shape(result) = shape(operand) ngoại trừ:
    • dim(result, scatter_dimension) = dim(operand, scatter_dimension) / dim(process_groups, 1).
  • (C9) element_type(result) = E.

Ví dụ

// 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]]

 Các ví dụ khác

reduce_window

Ngữ nghĩa

Áp dụng hàm giảm body cho các cửa sổ inputsinit_values, đồng thời tạo ra results.

Sơ đồ sau đây cho thấy cách các phần tử trong results... được tính toán từ inputs... bằng một ví dụ cụ thể.

reduce_window

Cụ thể hơn, results...[result_index] = reduce(windows, init_values, axes(inputs...), body) (xem reduce) trong đó:

  • 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).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) inputs số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C1-C4), (C6), (C8), (C10), (C12), (C13), (C15)
(I2) init_values số lượng biến đổi của tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C13)
(I3) window_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C4), (C5), (C15)
(I4) window_strides Hằng số tensor 1 chiều thuộc loại si64 (C6), (C7), (C15)
(I5) base_dilations Hằng số tensor 1 chiều thuộc loại si64 (C8), (C9), (C15)
(I6) window_dilations Hằng số tensor 1 chiều thuộc loại si64 (C10), (C11), (C15)
(I7) padding Hằng số tensor 2 chiều thuộc loại si64 (C12), (C15)
(I8) body hàm (C13)

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C14-C16)

Giới hạn

  • (C1) 0 < size(inputs) = size(init_values) = size(results) = N.
  • (C2) same(shape(inputs...)).
  • (C3) element_type(inputs...) = element_type(init_values...).
  • (C4) size(window_dimensions) = rank(inputs[0]).
  • (C5) 0 < window_dimensions.
  • (C6) size(window_strides) = rank(inputs[0]).
  • (C7) 0 < window_strides.
  • (C8) size(base_dilations) = rank(inputs[0]).
  • (C9) 0 < base_dilations.
  • (C10) size(window_dilations) = rank(inputs[0]).
  • (C11) 0 < window_dilations.
  • (C12) shape(padding) = [rank(inputs[0]), 2].
  • (C13) body có loại (tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>) trong đó is_promotable(element_type(inputs[i]), Ei).
  • (C14) same(shape(results...)).
  • (C15) shape(results[0]) = num_windows trong đó:
    • 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 cho tất cả i[0,N).

Ví dụ

// %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]]

 Các ví dụ khác

phần dư

Ngữ nghĩa

Thực hiện phần dư theo phần tử của các tensor số bị chia lhs và số chia rhs, đồng thời tạo ra một tensor result.

Cụ thể hơn, dấu của kết quả được lấy từ số bị chia và giá trị tuyệt đối của kết quả luôn nhỏ hơn giá trị tuyệt đối của số chia. Số dư được tính là lhs - d * rhs, trong đó d được tính theo công thức:

  • Đối với số nguyên: stablehlo.divide(lhs, rhs).
  • Đối với số thực: division(lhs, rhs) từ IEEE-754 có thuộc tính làm tròn roundTowardZero.
  • Đối với số phức: Sẽ xác định sau (#997).
  • Đối với các loại được lượng tử hoá:
    • dequantize_op_quantize(remainder, lhs, rhs, type(result)).

Đối với các kiểu phần tử dấu phẩy động, thao tác này trái ngược với thao tác remainder trong quy cách IEEE-754, trong đó d là một giá trị nguyên gần với giá trị chính xác của lhs/rhs với các mối quan hệ ràng buộc với số chẵn.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)
(I2) rhs tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

replica_id

Ngữ nghĩa

Tạo replica_id của quy trình hiện tại.

Kết quả đầu ra

Tên Loại
result Tensor 0 chiều thuộc loại ui32

Ví dụ

%result = "stablehlo.replica_id"() : () -> tensor<ui32>

 Các ví dụ khác

reshape

Ngữ nghĩa

Thực hiện việc định hình lại tensor operand thành tensor result. Về mặt khái niệm, việc này tương đương với việc giữ nguyên cùng một biểu diễn chuẩn tắc nhưng có thể thay đổi hình dạng, ví dụ: từ tensor<2x3xf32> thành tensor<3x2xf32> hoặc tensor<6xf32>.

Nói một cách chính thức hơn, result[result_index] = operand[operand_index] trong đó result_indexoperand_index có cùng vị trí trong thứ tự từ điển của index_space(result)index_space(operand).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor được lượng tử hoá (C1-C3)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá (C1-C3)

Giới hạn

  • (C1) element_type(result) được cho bởi:
    • element_type(operand), nếu !is_per_axis_quantized(operand).
    • element_type(operand), ngoại trừ quantization_dimension(operand)quantization_dimension(result) có thể khác nhau.
  • (C2) size(operand) = size(result).
  • (C3) Nếu 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).

Ví dụ

// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.reshape"(%operand) : (tensor<2x3xi32>) -> tensor<3x2xi32>
// %result: [[1, 2], [3, 4], [5, 6]]

 Các ví dụ khác

đảo ngược

Ngữ nghĩa

Đảo ngược thứ tự của các phần tử trong operand dọc theo dimensions đã chỉ định và tạo ra một tenxơ result. Một cách chính thức hơn, result[result_index] = operand[operand_index] trong đó:

  • operand_index[d] = dim(result, d) - result_index[d] - 1 nếu d trong dimensions.
  • Nếu không thì là operand_index[d] = result_index[d].

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C3)
(I2) dimensions Hằng số tensor 1 chiều thuộc loại si64 (C2), (C3)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C3)

Giới hạn

  • (C1) type(operand) = type(result).
  • (C2) is_unique(dimensions).
  • (C3) 0 <= dimensions < rank(result).

Ví dụ

// %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]]

 Các ví dụ khác

rng

Ngữ nghĩa

Tạo số ngẫu nhiên bằng thuật toán rng_distribution và tạo ra một tenxơ result có hình dạng nhất định shape.

Nếu rng_distribution = UNIFORM, thì các số ngẫu nhiên được tạo theo phân phối đồng đều trong khoảng [a, b). Nếu a >= b, hành vi sẽ không xác định.

Nếu rng_distribution = NORMAL, thì các số ngẫu nhiên được tạo theo phân phối chuẩn với giá trị trung bình = a và độ lệch chuẩn = b. Nếu b < 0, hành vi sẽ không xác định.

Cách chính xác để tạo số ngẫu nhiên được xác định theo quá trình triển khai. Ví dụ: chúng có thể là hàm xác định hoặc không, và có thể sử dụng hoặc không sử dụng trạng thái ẩn.

Trong các cuộc trò chuyện với nhiều bên liên quan, thao tác này đã được coi là không còn hiệu quả, vì vậy, trong tương lai, chúng tôi dự định sẽ loại bỏ thao tác này (#597).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) a Tensor 0 chiều thuộc loại số nguyên, boolean hoặc dấu phẩy động (C1), (C2)
(I2) b Tensor 0 chiều thuộc loại số nguyên, boolean hoặc dấu phẩy động (C1), (C2)
(I3) shape Hằng số tensor 1 chiều thuộc loại si64 (C3)
(I4) rng_distribution enum của UNIFORMNORMAL (C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên, boolean hoặc dấu phẩy động (C1-C3)

Giới hạn

  • (C1) element_type(a) = element_type(b) = element_type(result).
  • (C2) Nếu rng_distribution = NORMAL, thì is_float(a).
  • (C3) shape(result) = shape.

Ví dụ

// %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

Ngữ nghĩa

Trả về một output chứa đầy các bit ngẫu nhiên đồng nhất và trạng thái đầu ra được cập nhật output_state bằng cách sử dụng thuật toán trình tạo số ngẫu nhiên giả rng_algorithm cho một trạng thái ban đầu initial_state. Đầu ra được đảm bảo là hàm xác định của initial_state, nhưng không được đảm bảo là xác định giữa các phương thức triển khai.

rng_algorithm là một trong những giá trị sau:

  • DEFAULT: Thuật toán do quá trình triển khai xác định.
  • THREE_FRY: Biến thể do quá trình triển khai xác định của thuật toán Threefry.*
  • PHILOX: Biến thể do quá trình triển khai xác định của thuật toán Philox.*

* Xem: Salmon và cộng sự, SC 2011. Số ngẫu nhiên song song: dễ dàng như đếm 1, 2, 3.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) rng_algorithm enum của DEFAULT, THREE_FRYPHILOX (C2)
(I2) initial_state Tensor 1 chiều thuộc loại ui64 (C1), (C2)

Kết quả đầu ra

Tên Loại Giới hạn
output_state Tensor 1 chiều thuộc loại ui64 (C1)
output tensor thuộc loại số nguyên hoặc dấu phẩy động

Giới hạn

  • (C1) type(initial_state) = type(output_state).
  • (C2) size(initial_state) được xác định là:
    • do quá trình triển khai xác định nếu rng_algorithm = DEFAULT.
    • 2 nếu rng_algorithm = THREE_FRY.
    • 2 hoặc 3 nếu rng_algorithm = PHILOX.

Ví dụ

// %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

Ngữ nghĩa

Thực hiện thao tác làm tròn từng phần tử về số nguyên gần nhất, phá vỡ các mối liên kết từ 0, trên tenxơ operand và tạo ra tenxơ result. Triển khai thao tác roundToIntegralTiesToAway theo quy cách IEEE-754. Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(round_nearest_afz, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

round_nearest_even

Ngữ nghĩa

Thực hiện việc làm tròn từng phần tử về số nguyên gần nhất, phá vỡ các mối liên kết về số nguyên chẵn, trên tenxơ operand và tạo ra một tenxơ result. Triển khai thao tác roundToIntegralTiesToEven theo quy cách IEEE-754. Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(round_nearest_even, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

rsqrt

Ngữ nghĩa

Thực hiện phép toán căn bậc hai nghịch đảo theo phần tử trên tenxơ operand và tạo ra tenxơ result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: rSqrt từ IEEE-754.
  • Đối với số phức: căn bậc hai nghịch đảo phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(rsqrt, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

phân tán

Ngữ nghĩa

Tạo ra các tensor results bằng với các tensor inputs, ngoại trừ một số lát cắt do scatter_indices chỉ định được cập nhật bằng các giá trị updates bằng cách sử dụng update_computation.

Sơ đồ sau đây cho thấy cách các phần tử trong updates... liên kết với các phần tử trong results... bằng một ví dụ cụ thể. Biểu đồ này chọn một vài chỉ mục updates... làm ví dụ và giải thích chi tiết chỉ mục results... mà chúng tương ứng.

phân tán

Cụ thể hơn, đối với mọi update_index trong 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 được xác định là:
    • scatter_indices[si0, ..., :, ..., siN] trong đó si là các phần tử riêng lẻ trong update_scatter_index: được chèn vào chỉ mục index_vector_dim, nếu index_vector_dim < rank(scatter_indices).
    • Nếu không thì là [scatter_indices[update_scatter_index]].
  • Đối với d_input trong axes(inputs[0]),
    • full_start_index[d_input] = start_index[d_start] nếu d_input = scatter_dims_to_operand_dims[d_start].
    • Nếu không thì là full_start_index[d_input] = 0.
  • Đối với d_input trong axes(inputs[0]),
    • full_batching_index[d_input] = update_scatter_index[d_start - (d_start < index_vector_dim ? 0 : 1)] nếu d_input = input_batching_dims[i_batching]d_start = scatter_indices_batching_dims[i_batching].
    • Nếu không thì là full_batching_index[d_input] = 0.
  • update_window_index = update_index[update_window_dims...].
  • full_window_index = [wi0, ..., 0, ..., wiN] trong đó wi là các phần tử riêng lẻ trong update_window_index0 được chèn ở các chỉ mục từ inserted_window_dimsinput_batching_dims.
  • result_index = full_start_index + full_batching_index + full_window_index.

Cho rằng results = exec(schedule, inputs), trong đó:

  • schedule là một hoán vị do quá trình triển khai xác định của index_space(updates[0]).
  • exec([update_index, ...], results) = exec([...], updated_results) nơi:
    • Nếu result_index nằm trong phạm vi của 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 là bản sao của results với results...[result_index] được đặt thành updated_values....
    • Hoặc
    • updated_results = results.
  • exec([], results) = results.

Nếu indices_are_sortedtrue thì quá trình triển khai có thể giả định rằng scatter_indices được sắp xếp theo scatter_dims_to_operand_dims, nếu không thì hành vi sẽ không xác định. Nói một cách chính thức hơn, đối với tất cả i1 < i2 từ indices(result), full_start_index(i1) <= full_start_index(i2).

Nếu unique_indicestrue thì quá trình triển khai có thể giả định rằng tất cả các chỉ mục result_index đang được phân tán đều là duy nhất. Nếu unique_indicestrue nhưng các chỉ mục được phân tán không phải là duy nhất thì hành vi này sẽ không xác định.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) inputs số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C2), (C4-C6), (C11), (C13), (C18), (C21), (C23-C24)
(I2) scatter_indices tensor thuộc loại số nguyên (C4), (C15), (C19), (C22)
(I3) updates số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C3-C6), (C8)
(I4) update_window_dims Hằng số tensor 1 chiều thuộc loại si64 (C2), (C4), (C7-C8)
(I5) inserted_window_dims Hằng số tensor 1 chiều thuộc loại si64 (C2), (C4), (C9-C11)
(I6) input_batching_dims Hằng số tensor 1 chiều thuộc loại si64 (C2), (C4), (C9), (C12-13), (C17-18), (C20)
(I7) scatter_indices_batching_dims Hằng số tensor 1 chiều thuộc loại si64 (C14-C18)
(I8) scatter_dims_to_operand_dims Hằng số tensor 1 chiều thuộc loại si64 (C19-C21)
(I9) index_vector_dim hằng số thuộc loại si64 (C4), (C16), (C19), (C22)
(I10) indices_are_sorted hằng số thuộc loại i1
(I11) unique_indices hằng số thuộc loại i1
(I12) update_computation hàm (C23)

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C24-C25)

Giới hạn

  • (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) trong đó:
    • update_scatter_dim_sizes = shape(scatter_indices), ngoại trừ kích thước phương diện của scatter_indices tương ứng với index_vector_dim không được đưa vào.
    • update_window_dim_sizes <= shape(inputs[0]), ngoại trừ kích thước phương diện trong inputs[0] tương ứng với inserted_window_dimsinput_batching_dims không được đưa vào.
    • combine đặt update_scatter_dim_sizes tại các trục tương ứng với update_scatter_dimsupdate_window_dim_sizes tại các trục tương ứng với update_window_dims.
  • (C5) 0 < size(inputs) = size(updates) = N.
  • (C6) element_type(updates...) = element_type(inputs...).
  • (C7) is_unique(update_window_dims) and is_sorted(update_window_dims).
  • (C8) 0 <= update_window_dims < rank(updates[0]).
  • (C9) is_unique(concatenate(inserted_window_dims, input_batching_dims))
  • (C10) is_sorted(inserted_window_dims).
  • (C11) 0 <= inserted_window_dims < rank(inputs[0]).
  • (C12) is_sorted(input_batching_dims).
  • (C13) 0 <= input_batching_dims < rank(inputs[0])).
  • (C14) is_unique(scatter_indices_batching_dims).
  • (C15) 0 <= scatter_indices_batching_dims < rank(scatter_indices).
  • (C16) index_vector_dim not in scatter_indices_batching_dims.
  • (C17) size(input_batching_dims) == size(scatter_indices_batching_dims).
  • (C18) dim(inputs[0], input_batching_dims...) = dim(scatter_indices, scatter_indices_batching_dims...).
  • (C19) size(scatter_dims_to_operand_dims) = index_vector_dim < rank(scatter_indices) ? dim(scatter_indices, index_vector_dim) : 1.
  • (C20) is_unique(concatenate(scatter_dims_to_operand_dims, input_batching_dims)).
  • (C21) 0 <= scatter_dims_to_operand_dims < rank(inputs[0]).
  • (C22) 0 <= index_vector_dim <= rank(scatter_indices).
  • (C23) update_computation có kiểu (tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>), trong đó is_promotable(element_type(inputs[i]), Ei).
  • (C24) shape(inputs...) = shape(results...).
  • (C25) element_type(results[i]) = Ei cho tất cả i trong [0,N).

Ví dụ

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

 Các ví dụ khác

chọn

Ngữ nghĩa

Tạo ra một tensor result trong đó mỗi phần tử được chọn từ tensor on_true hoặc on_false dựa trên giá trị của phần tử tương ứng của pred. Cụ thể hơn, result[result_index] = pred_element ? on_true[result_index] : on_false[result_index], trong đó pred_element = rank(pred) = 0 ? pred[] : pred[result_index]. Đối với các loại được định lượng, hãy thực hiện dequantize_select_quantize(pred, on_true, on_false, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) pred tensor thuộc loại i1 (C1)
(I2) on_true tensor hoặc tensor được lượng tử hoá theo từng tensor (C1-C2)
(I3) on_false tensor hoặc tensor lượng tử hoá trên mỗi tensor (C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C2)

Giới hạn

  • (C1) rank(pred) = 0 or shape(pred) = shape(on_true).
  • (C2) baseline_type(on_true) = baseline_type(on_false) = baseline_type(result).

Ví dụ

// %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]]

 Các ví dụ khác

select_and_scatter

Ngữ nghĩa

Phân tán các giá trị từ tenxơ source bằng cách sử dụng scatter dựa trên kết quả của reduce_window của tenxơ input bằng cách sử dụng select và tạo ra một tenxơ result.

Sơ đồ sau đây cho thấy cách tính toán các phần tử trong result từ operandsource bằng một ví dụ cụ thể.

select_and_scatter

Một cách trang trọng hơn:

  • selected_values = reduce_window_without_init(...) với các đầu vào sau:

    • inputs = [operand].
    • window_dimensions, window_stridespadding được sử dụng nguyên trạng.
    • base_dilations = windows_dilations = 1.
    • body được xác định là:
    def body(arg0: tensor<E>, arg1: tensor<E>) -> tensor<E>:
      return select(arg0, arg1) ? arg0 : arg1;
    

    trong đó E = element_type(operand)reduce_window_without_init hoạt động giống hệt như reduce_window, ngoại trừ schedule của reduce cơ bản (xem reduce) không bao gồm các giá trị init. Hiện tại, chưa xác định được điều gì sẽ xảy ra nếu cửa sổ tương ứng không có giá trị (#731).

  • result[result_index] = reduce([source_values], [init_value], [0], scatter) trong đó:

    • source_values = [source[source_index] for source_index in source_indices].
    • selected_index(source_index) = operand_index nếu selected_values[source_index] có phần tử operand từ operand_index.
    • source_indices = [source_index for source_index in indices(source) if selected_index(source_index) = result_index].

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1-C4), (C6), (C8-C11)
(I2) source tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C2)
(I3) init_value Tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor (C3)
(I4) window_dimensions Hằng số tensor 1 chiều thuộc loại si64 (C2), (C4), (C5)
(I5) window_strides Hằng số tensor 1 chiều thuộc loại si64 (C2), (C6), (C7)
(I6) padding Hằng số tensor 2 chiều thuộc loại si64 (C2), (C8)
(I7) select hàm (C9)
(I8) scatter hàm (C10)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá theo từng tensor (C11 – C12)

Giới hạn

  • (C1) element_type(operand) = element_type(source).
  • (C2) shape(source) = num_windows trong đó:
    • padded_operand_shape = padding[:, 0] + shape(operand) + padding[:, 1].
    • is_empty_window = padded_operand_shape = 0 || window_dimensions > padded_operand_shape.
    • num_windows = is_empty_window ? 0 : floor((padded_operand_shape - window_dimensions) / window_strides) + 1.
  • (C3) element_type(init_value) = element_type(operand).
  • (C4) size(window_dimensions) = rank(operand).
  • (C5) 0 < window_dimensions.
  • (C6) size(window_strides) = rank(operand).
  • (C7) 0 < window_strides.
  • (C8) shape(padding) = [rank(operand), 2].
  • (C9) select có loại (tensor<E>, tensor<E>) -> tensor<i1> trong đó E = element_type(operand).
  • (C10) scatter có loại (tensor<E>, tensor<E>) -> tensor<E> trong đó is_promotable(element_type(operand), E).
  • (C11) shape(operand) = shape(result).
  • (C12) element_type(result) = E.

Ví dụ

// %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]]

 Các ví dụ khác

gửi

Ngữ nghĩa

Gửi inputs đến một kênh channel_id. Sau đó, các đầu vào sẽ được gửi đến các thiết bị khác theo thứ tự được chỉ định bởi source_target_pairs. Thao tác này tạo ra một mã thông báo result.

Nếu is_host_transfertrue, thì thao tác sẽ chuyển dữ liệu đến máy chủ lưu trữ. Nếu không, dữ liệu sẽ được chuyển sang một thiết bị khác dựa trên các giá trị của source_target_pairs. Cờ này trùng lặp thông tin được cung cấp trong channel_type, vì vậy, trong tương lai, chúng tôi dự định chỉ giữ lại một trong hai cờ này (#666). Nếu is_host_transfer = falsesource_target_pairsNone hoặc trống, thì đó được coi là hành vi không xác định.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) inputs số lượng tensor hoặc tensor lượng tử hoá có thể thay đổi
(I2) token token
(I3) channel_id hằng số thuộc loại si64
(I4) channel_type enum của DEVICE_TO_DEVICEDEVICE_TO_HOST (C5)
(I5) is_host_transfer hằng số thuộc loại i1 (C5-C6)
(I6) source_target_pairs Hằng số tensor 2 chiều thuộc loại si64 (C1-C4), (C6)

Kết quả đầu ra

Tên Loại
result token

Giới hạn

  • (C1) dim(source_target_pairs, 1) = 2.
  • (C2) is_unique(source_target_pairs[:, 0]).
  • (C3) is_unique(source_target_pairs[:, 1]).
  • (C4) 0 <= source_target_pairs < N, trong đó N được xác định là:
    • num_replicas nếu bạn sử dụng cross_replica.
    • num_partitions nếu bạn sử dụng cross_partition.
  • (C5) channel_type được xác định là:
    • DEVICE_TO_HOST nếu is_host_transfer = true,
    • Nếu không thì là DEVICE_TO_DEVICE.

Ví dụ

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

 Các ví dụ khác

shift_left

Ngữ nghĩa

Thực hiện thao tác dịch trái theo phần tử trên tensor lhs theo số lượng bit rhs và tạo ra tensor result.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại số nguyên (C1)
(I2) rhs tensor thuộc loại số nguyên (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên (C1)

Giới hạn

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

Ví dụ

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

 Các ví dụ khác

shift_right_arithmetic

Ngữ nghĩa

Thực hiện phép toán dịch chuyển số học sang phải theo từng phần tử trên tenxơ lhs theo số lượng bit rhs và tạo ra một tenxơ result.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại số nguyên (C1)
(I2) rhs tensor thuộc loại số nguyên (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

shift_right_logical

Ngữ nghĩa

Thực hiện thao tác dịch chuyển logic sang phải theo từng phần tử trên tenxơ lhs theo rhs số lượng bit và tạo ra một tenxơ result.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại số nguyên (C1)
(I2) rhs tensor thuộc loại số nguyên (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên (C1)

Giới hạn

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

Ví dụ

// %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]

 Các ví dụ khác

biển báo

Ngữ nghĩa

Trả về dấu của phần tử operand theo từng phần tử và tạo ra một tensor result. Cụ thể hơn, đối với mỗi phần tử x, ngữ nghĩa có thể được biểu thị bằng cú pháp Python như sau:

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)))

Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(sign, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor của số nguyên có dấu, dấu phẩy động hoặc loại phức hợp hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor của số nguyên có dấu, dấu phẩy động hoặc loại phức hợp hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// 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]

 Các ví dụ khác

sin

Ngữ nghĩa

Thực hiện phép toán sin trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: sin từ IEEE-754.
  • Đối với số phức: sin phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(sine, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

lát cắt

Ngữ nghĩa

Trích xuất một lát cắt từ operand bằng cách sử dụng các chỉ mục bắt đầu được tính toán tĩnh và tạo ra một tensor result. start_indices chứa chỉ mục bắt đầu của lát cắt cho từng phương diện, limit_indices chứa chỉ mục kết thúc (loại trừ) cho lát cắt cho từng phương diện và strides chứa bước nhảy cho từng phương diện.

Cụ thể hơn, result[result_index] = operand[operand_index] trong đó operand_index = start_indices + result_index * strides.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1-C3), (C5)
(I2) start_indices Hằng số tensor 1 chiều thuộc loại si64 (C2), (C3), (C5)
(I3) limit_indices Hằng số tensor 1 chiều thuộc loại si64 (C2), (C3), (C5)
(I4) strides Hằng số tensor 1 chiều thuộc loại si64 (C2), (C4)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá trên mỗi tensor (C1), (C5)

Giới hạn

  • (C1) element_type(operand) = element_type(result).
  • (C2) size(start_indices) = size(limit_indices) = size(strides) = rank(operand).
  • (C3) 0 <= start_indices <= limit_indices <= shape(operand).
  • (C4) 0 < strides.
  • (C5) shape(result) = ceil((limit_indices - start_indices) / strides).

Ví dụ

// %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]
//           ]

 Các ví dụ khác

sắp xếp

Ngữ nghĩa

Sắp xếp các phần tử 1 chiều của inputs dọc theo phương diện dimension với nhau, theo comparator và tạo ra results.

Không giống như các đầu vào tương tự trong các thao tác khác, dimension cho phép các giá trị âm, với ngữ nghĩa được mô tả bên dưới. Trong tương lai, điều này có thể không được phép vì lý do nhất quán (#1377).

Nếu is_stable là true, thì việc sắp xếp sẽ ổn định, tức là thứ tự tương đối của các phần tử được bộ so sánh coi là bằng nhau sẽ được giữ nguyên. Trong trường hợp chỉ có một đầu vào, hai phần tử e1e2 được bộ so sánh coi là bằng nhau khi và chỉ khi comparator(e1, e2) = comparator(e2, e1) = false. Hãy xem cách thức chính thức hoá bên dưới để biết cách tổng quát hoá điều này cho nhiều đầu vào.

Cụ thể hơn, đối với mọi result_index trong index_space(results[0]):

  • adjusted_dimension = dimension >= 0 ? dimension : rank(inputs[0]) + dimension.
  • result_slice = [ri0, ..., :, ..., riR-1] trong đó riN là các phần tử riêng lẻ trong result_index: được chèn vào adjusted_dimension.
  • inputs_together = (inputs[0]..., ..., inputs[N-1]...).
  • results_together[result_slice] = sort(inputs_together[result_slice], comparator_together).
  • trong đó sort sắp xếp một lát cắt 1 chiều theo thứ tự không giảm, dự kiến comparator_together sẽ trả về true nếu đối số ở phía bên trái nhỏ hơn đối số thứ hai ở phía bên phải.
  • 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.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) inputs số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C1 – C5)
(I2) dimension hằng số thuộc loại si64 (C4)
(I3) is_stable hằng số thuộc loại i1
(I4) comparator hàm (C5)

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor (C2), (C3)

Giới hạn

  • (C1) 0 < size(inputs).
  • (C2) type(inputs...) = type(results...).
  • (C3) same(shape(inputs...) + shape(results...)).
  • (C4) -R <= dimension < R, trong đó R = rank(inputs[0]).
  • (C5) comparator có loại (tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>, trong đó Ei = element_type(inputs[i]).

Ví dụ

// %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]]

 Các ví dụ khác

sqrt

Ngữ nghĩa

Thực hiện phép toán căn bậc hai trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: squareRoot từ IEEE-754.
  • Đối với số phức: căn bậc hai của số phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(sqrt, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

trừ

Ngữ nghĩa

Thực hiện phép trừ từng phần tử của hai tensor lhsrhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số nguyên: phép trừ số nguyên.
  • Đối với số thực: subtraction từ IEEE-754.
  • Đối với số phức: phép trừ số phức.
  • Đối với các loại được lượng tử hoá:
    • dequantize_op_quantize(subtract, lhs, rhs, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)
(I2) rhs tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]]

 Các ví dụ khác

tan

Ngữ nghĩa

Thực hiện phép toán tiếp tuyến theo phần tử trên tensor operand và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: tan từ IEEE-754.
  • Đối với số phức: hàm tang số phức.
  • Đối với các loại được lượng tử hoá: dequantize_op_quantize(tan, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

// %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]
//          ]

 Các ví dụ khác

tanh

Ngữ nghĩa

Thực hiện phép toán tan hyperbolic trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với số thực: tanh từ IEEE-754.
  • Đối với số phức: tang hyperbol của số phức.
  • Đối với các loại được lượng tử hoá:
    • dequantize_op_quantize(tanh, operand, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

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

Ví dụ

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

 Các ví dụ khác

chuyển vị

Ngữ nghĩa

Hoán vị các phương diện của tensor operand bằng cách sử dụng permutation và tạo ra một tensor result. Cụ thể hơn, result[result_index] = operand[operand_index] trong đó result_index[d] = operand_index[permutation[d]].

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor hoặc tensor được lượng tử hoá (C1-C4)
(I2) permutation Hằng số tensor 1 chiều thuộc loại si64 (C2-C4)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor được lượng tử hoá (C1), (C3-C4)

Giới hạn

  • (C1) element_type(result) được cho bởi:
    • element_type(operand), nếu !is_per_axis_quantized(operand).
    • element_type(operand), ngoại trừ quantization_dimension(operand)quantization_dimension(result) có thể khác nhau.
  • (C2) permutation là một hoán vị của range(rank(operand)).
  • (C3) shape(result) = dim(operand, permutation...).
  • (C4) Nếu is_per_axis_quantized(result), thì quantization_dimension(operand) = permutation(quantization_dimension(result)).

Ví dụ

// %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]]
//          ]

 Các ví dụ khác

triangular_solve

Ngữ nghĩa

Giải các lô hệ phương trình tuyến tính có ma trận hệ số tam giác trên hoặc dưới.

Nói một cách chính thức hơn, cho ab, result[i0, ..., iR-3, :, :] là giải pháp cho op(a[i0, ..., iR-3, :, :]) * x = b[i0, ..., iR-3, :, :] khi left_sidetrue hoặc x * op(a[i0, ..., iR-3, :, :]) = b[i0, ..., iR-3, :, :] khi left_sidefalse, giải quyết cho biến x trong đó op(a) được xác định bằng transpose_a, có thể là một trong những điều sau:

  • NO_TRANSPOSE: Thực hiện thao tác bằng cách sử dụng a nguyên trạng.
  • TRANSPOSE: Thực hiện thao tác trên chuyển vị của a.
  • ADJOINT: Thực hiện thao tác trên chuyển vị liên hợp của a.

Dữ liệu đầu vào chỉ được đọc từ tam giác dưới của a, nếu lowertrue hoặc tam giác trên của a, nếu không. Dữ liệu đầu ra được trả về trong cùng một tam giác; các giá trị trong tam giác còn lại do quá trình triển khai xác định.

Nếu unit_diagonal là true, thì quá trình triển khai có thể giả định rằng các phần tử đường chéo của a bằng 1, nếu không, hành vi sẽ không xác định.

Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(lambda x, y: triangular_solve(x, y, left_side, lower, unit_diagonal, transpose_a), a, b, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) a tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1-C3)
(I2) b tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1-C4)
(I3) left_side hằng số thuộc loại i1 (C3)
(I4) lower hằng số thuộc loại i1
(I5) unit_diagonal hằng số thuộc loại i1
(I6) transpose_a enum của NO_TRANSPOSE, TRANSPOSEADJOINT

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)

Giới hạn

  • (C1) baseline_element_type(a) = baseline_element_type(b).
  • (C2) 2 <= rank(a) = rank(b) = R.
  • (C3) Mối quan hệ giữa shape(a)shape(b) được xác định như sau:
    • 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).

Ví dụ

// %a = [
//       [1.0, 0.0, 0.0],
//       [2.0, 4.0, 0.0],
//       [3.0, 5.0, 6.0]
//      ]
// %b = [
//       [2.0, 0.0, 0.0],
//       [4.0, 8.0, 0.0],
//       [6.0, 10.0, 12.0]
//      ]
%result = "stablehlo.triangular_solve"(%a, %b) {
  left_side = true,
  lower = true,
  unit_diagonal = false,
  transpose_a = #stablehlo<transpose NO_TRANSPOSE>
} : (tensor<3x3xf32>, tensor<3x3xf32>) -> tensor<3x3xf32>
// %result: [
//           [2.0, 0.0, 0.0],
//           [0.0, 2.0, 0.0],
//           [0.0, 0.0, 2.0]
//          ]

tuple

Ngữ nghĩa

Tạo một bộ result từ các giá trị val.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) val số lượng giá trị thay đổi (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tuple (C1)

Giới hạn

  • (C1) result có loại tuple<E0, ..., EN-1> trong đó Ei = type(val[i]).

Ví dụ

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

 Các ví dụ khác

uniform_dequantize

Ngữ nghĩa

Thực hiện quá trình chuyển đổi từng phần tử của tensor được lượng tử hoá operand thành tensor dấu phẩy động result theo các tham số lượng tử hoá do loại operand xác định.

Nói một cách trang trọng hơn, result = dequantize(operand).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor được lượng tử hoá (C1), (C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại dấu phẩy động (C1), (C2)

Giới hạn

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

Ví dụ

// %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

Ngữ nghĩa

Thực hiện quá trình chuyển đổi từng phần tử của tensor dấu phẩy động hoặc tensor lượng tử hoá operand thành tensor lượng tử hoá result theo các tham số lượng tử hoá do loại result xác định.

Nói một cách trang trọng hơn,

  • Nếu is_float(operand):
    • result = quantize(operand, type(result)).
  • Nếu is_quantized(operand):
    • float_result = dequantize(operand).
    • result = quantize(float_result, type(result)).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand tensor thuộc loại dấu phẩy động hoặc lượng tử hoá (C1), (C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor được lượng tử hoá (C1), (C2)

Giới hạn

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

Ví dụ

// %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]

trong khi

Ngữ nghĩa

Tạo đầu ra từ việc thực thi hàm body 0 hoặc nhiều lần trong khi hàm cond xuất ra true. Nói một cách chính thức hơn, ngữ nghĩa có thể được biểu thị bằng cú pháp Python như sau:

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

Hành vi của vòng lặp vô hạn sẽ được xác định sau (#383).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand số lượng giá trị thay đổi (C1-C3)
(I2) cond hàm (C1)
(I3) body hàm (C2)

Kết quả đầu ra

Tên Loại Giới hạn
results số lượng giá trị thay đổi (C3)

Giới hạn

  • (C1) cond có kiểu (T0, ..., TN-1) -> tensor<i1>, trong đó Ti = type(operand[i]).
  • (C2) body có loại (T0, ..., TN-1) -> (T0, ..., TN-1), trong đó Ti = type(operand[i]).
  • (C3) type(results...) = type(operand...).

Ví dụ

// %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

 Các ví dụ khác

xor

Ngữ nghĩa

Thực hiện phép toán XOR theo phần tử của hai tensor lhsrhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:

  • Đối với giá trị boolean: phép XOR logic.
  • Đối với số nguyên: đảo bit XOR.

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) lhs tensor thuộc loại boolean hoặc số nguyên (C1)
(I2) rhs tensor thuộc loại boolean hoặc số nguyên (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor thuộc loại boolean hoặc số nguyên (C1)

Giới hạn

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

Ví dụ

// 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]]

 Các ví dụ khác

Khả năng tương tác giữa các phương ngữ

Hiện tại, các chương trình StableHLO đôi khi chứa các thao tác không được StableHLO xác định.

Mô-đun, Hàm, Lệnh gọi và Trả về

StableHLO sử dụng các thao tác MLIR ở thượng nguồn cho ModuleOp, FuncOp, CallOp và ReturnOp. Điều này được thực hiện để có khả năng tương tác tốt hơn với cơ chế MLIR hiện có, vì nhiều lượt truyền hữu ích được viết nhắm đến FuncOp và ModuleOp, đồng thời nhiều quy trình biên dịch dự kiến sẽ có các thao tác này. Đảm bảo khả năng tương thích đầy đủ được áp dụng cho các thao tác này. Nếu có bất kỳ thay đổi nào về các thao tác này theo cách không tương thích (tức là xoá), thì các thao tác tương đương StableHLO sẽ được thêm vào để duy trì khả năng tương thích.

CHLO

Opset CHLO chứa các thao tác cấp cao hơn phân tách thành StableHLO. Hiện tại, không có gì đảm bảo về khả năng tương thích cho CHLO. Để đảm bảo khả năng tương thích, bạn phải sử dụng chlo-legalize-to-stablehlo pass trước khi chuyển đổi tuần tự.

Thao tác với hình dạng

Đây là một trường hợp sử dụng phổ biến trong cộng đồng để sử dụng một số thao tác từ các phương ngữ MLIR cốt lõi trong các chương trình StableHLO động nhằm thực hiện các phép tính hình dạng. Phổ biến nhất là các thao tác shape phương ngữ như shape_of hoặc num_elements, các thao tác tensor phương ngữ như dim hoặc from_elements và loại index tích hợp.

Dynamism RFC > O2 cho biết những loại này nằm ngoài phạm vi, tuy nhiên, một số loại index được hỗ trợ cho mục đích tương tác. Không có gì đảm bảo về khả năng tương thích cho các thao tác hoặc loại này. Bạn có thể dùng lệnh truyền shape-legalize-to-stablehlo để chuyển đổi các thao tác này thành các thao tác StableHLO được hỗ trợ đầy đủ.

Các thao tác không dùng nữa

Có một số thao tác StableHLO được kế thừa từ MHLO, những thao tác này không còn được dùng nữa và sắp bị loại bỏ khỏi StableHLO. Bạn có thể xem toàn bộ thông tin chi tiết về những lần xoá này trong StableHLO v1.0 Cleanup #2283. Vấn đề trên trình theo dõi cho những hoạt động không dùng nữa này là #2340.

Các thao tác này thuộc một số danh mục:

  • Danh mục "Không có trong HLO" của các hoạt động StableHLO – ban đầu, các hoạt động này thuộc opset StableHLO nhưng sau đó được coi là không phù hợp với opset này: broadcast, create_token, cross-replica-sum, dot, einsum, torch_index_select, unary_einsum (#3).
  • Các thao tác không dùng đến – Những thao tác này có thể hữu ích tại một thời điểm nào đó, nhưng các thao tác này chưa được phát triển đầy đủ hoặc các quy trình sử dụng những thao tác này đã được tái cấu trúc để không cần đến chúng nữa. Các API này bao gồm map, tuple (#598), get_tuple_element, rng, complex so sánh #560 và tích chập window_reversal (#1181).

Một số thao tác trong số này có thể dễ dàng bị xoá vì chúng có thể được biểu thị bằng các thao tác hiện có (broadcast, create_token, cross-replica-sum, dot, unary_einsum) và sẽ bị xoá sau khi hết thời gian tương thích hiện tại (6 tháng). Các thao tác khác vẫn đang được xem xét để loại bỏ (einsum, get_tuple_element, map, rng torch_index_select, tuple, complex so sánh, window_reversal). Tuỳ thuộc vào ý kiến phản hồi của cộng đồng, các thao tác này sẽ bị loại bỏ hoặc được thêm vào quy cách với sự hỗ trợ đầy đủ. Cho đến khi biết được các hoạt động này trong tương lai, chúng chỉ được đảm bảo khả năng tương thích trong 6 tháng.

Thực thi

Thực thi tuần tự

Một chương trình StableHLO được thực thi bằng cách cung cấp các giá trị đầu vào cho hàm main và tính toán các giá trị đầu ra. Các giá trị đầu ra của một hàm được tính bằng cách thực thi biểu đồ của các thao tác bắt nguồn từ thao tác return tương ứng.

Thứ tự thực thi được xác định theo cách triển khai miễn là thứ tự đó phù hợp với luồng dữ liệu, tức là nếu các thao tác được thực thi trước khi sử dụng. Trong StableHLO, tất cả các hoạt động có tác dụng phụ đều sử dụng một mã thông báo và tạo ra một mã thông báo (nhiều mã thông báo có thể được ghép kênh thành một mã thông báo thông qua after_all), do đó, thứ tự thực thi của các tác dụng phụ cũng được điều chỉnh theo luồng dữ liệu. Ví dụ: trong chương trình bên dưới, có 2 thứ tự thực thi có thể xảy ra: %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>
}

Cụ thể hơn, quy trình StableHLO là sự kết hợp của: 1) một chương trình StableHLO, 2) trạng thái hoạt động (chưa thực thi, đã thực thi) và 3) các giá trị trung gian mà quy trình đang xử lý. Quy trình này bắt đầu bằng các giá trị đầu vào cho hàm main, tiến hành thông qua biểu đồ của các thao tác cập nhật trạng thái hoạt động và các giá trị trung gian, đồng thời kết thúc bằng các giá trị đầu ra. Việc chính thức hoá thêm sẽ được xác định sau (#484).

Thực thi song song

Các chương trình StableHLO có thể được thực thi song song, được sắp xếp thành một lưới quy trình 2D gồm num_replicas theo num_partitions, cả hai đều có loại ui32.

Trong lưới quy trình StableHLO, num_replicas * num_partitions quy trình StableHLO đang thực thi cùng một lúc. Mỗi quy trình có một process_id = (replica_id, partition_id) duy nhất, trong đó replica_id trong replica_ids = range(num_replicas)partition_id trong partition_ids = range(num_partitions) đều có loại ui32.

Kích thước của lưới quy trình được biết một cách tĩnh cho mọi chương trình (trong tương lai, chúng tôi dự định đưa kích thước này vào một phần rõ ràng của các chương trình StableHLO #650), và vị trí trong lưới quy trình được biết một cách tĩnh cho mọi quy trình. Mỗi quy trình có quyền truy cập vào vị trí của quy trình đó trong lưới quy trình thông qua các thao tác replica_idpartition_id.

Trong lưới quy trình, các chương trình có thể giống nhau (theo kiểu "Một chương trình, nhiều dữ liệu"), có thể khác nhau (theo kiểu "Nhiều chương trình, nhiều dữ liệu") hoặc nằm ở giữa. Trong tương lai, chúng tôi dự định sẽ hỗ trợ các thành phần khác để xác định các chương trình StableHLO song song, bao gồm cả GSPMD (#619).

Trong lưới quy trình, các quy trình hầu hết độc lập với nhau – chúng có trạng thái hoạt động riêng, các giá trị đầu vào/trung gian/đầu ra riêng và hầu hết các thao tác đều được thực thi riêng giữa các quy trình, ngoại trừ một số ít thao tác tập thể được mô tả bên dưới.

Vì việc thực thi hầu hết các thao tác chỉ sử dụng các giá trị từ cùng một quy trình, nên thường không có gì mơ hồ khi tham chiếu đến các giá trị này theo tên của chúng. Tuy nhiên, khi mô tả ngữ nghĩa của các thao tác tập thể, điều đó là không đủ và điều đó dẫn đến ký hiệu name@process_id để chỉ giá trị name trong một quy trình cụ thể. (Theo quan điểm đó, name không đủ tiêu chuẩn có thể được xem là cách viết tắt của name@(replica_id(), partition_id())).

Thứ tự thực thi trên các quy trình được xác định theo cách triển khai, ngoại trừ quá trình đồng bộ hoá do giao tiếp điểm-điểm và các thao tác tập thể được giới thiệu như mô tả bên dưới.

Giao tiếp điểm-điểm

Các quy trình StableHLO có thể giao tiếp với nhau thông qua các kênh StableHLO. Kênh được biểu thị bằng một mã nhận dạng dương thuộc loại si64. Thông qua nhiều thao tác, bạn có thể gửi giá trị đến các kênh và nhận giá trị từ các kênh.

Việc chính thức hoá thêm, chẳng hạn như các mã nhận dạng kênh này đến từ đâu, cách các chương trình xử lý nhận biết chúng và loại đồng bộ hoá nào được chúng giới thiệu, sẽ được xác định sau (#484).

Truyền thông trực tuyến

Mọi quy trình StableHLO đều có quyền truy cập vào 2 giao diện truyền phát trực tuyến:

  • Infeed có thể đọc được.
  • Outfeed có thể được ghi vào.

Không giống như các kênh được dùng để giao tiếp giữa các quy trình và do đó có các quy trình ở cả hai đầu, infeeds và outfeeds có việc triển khai đầu kia do người dùng xác định.

Việc chính thức hoá thêm, chẳng hạn như cách giao tiếp truyền trực tuyến ảnh hưởng đến thứ tự thực thi và loại đồng bộ hoá nào được giới thiệu bởi giao tiếp truyền trực tuyến, sẽ được xác định sau (#484).

Collective ops

Có 6 thao tác tập thể trong StableHLO: all_gather, all_reduce, all_to_all, collective_broadcast, collective_permutereduce_scatter. Tất cả các thao tác này đều chia các quy trình trong lưới quy trình StableHLO thành các nhóm quy trình StableHLO và thực thi một phép tính chung trong mỗi nhóm quy trình, độc lập với các nhóm quy trình khác.

Trong mỗi nhóm quy trình, các thao tác tập thể có thể giới thiệu một rào cản đồng bộ hoá. Việc chính thức hoá thêm, chẳng hạn như giải thích rõ thời điểm chính xác diễn ra quá trình đồng bộ hoá này, cách chính xác mà các quy trình đạt đến rào cản này và điều gì sẽ xảy ra nếu chúng không đạt được, sẽ được xác định sau (#484).

Nếu nhóm quy trình liên quan đến giao tiếp giữa các phân vùng, tức là có các quy trình trong nhóm quy trình có mã phân vùng khác nhau, thì việc thực thi thao tác tập thể cần một kênh và thao tác tập thể phải cung cấp một channel_id dương thuộc loại si64. Giao tiếp giữa các bản sao không cần đến các kênh.

Các phép tính do các thao tác tập thể thực hiện là dành riêng cho từng thao tác và được mô tả trong các phần thao tác riêng lẻ ở trên. Tuy nhiên, các chiến lược mà lưới quy trình được chia thành các nhóm quy trình sẽ được chia sẻ giữa các thao tác này và được mô tả trong phần này. Cụ thể hơn, StableHLO hỗ trợ 4 chiến lược sau.

cross_replica

Chỉ có hoạt động giao tiếp giữa các bản sao diễn ra trong mỗi nhóm quy trình. Chiến lược này lấy replica_groups – danh sách các danh sách mã nhận dạng bản sao – và tính tích Đề các của replica_groups theo partition_ids. replica_groups phải có các phần tử riêng biệt và bao gồm tất cả replica_ids. Cụ thể hơn, sử dụng cú pháp 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

Ví dụ: đối với replica_groups = [[0, 1], [2, 3]]num_partitions = 2, cross_replica sẽ tạo ra [[(0, 0), (1, 0)], [(0, 1), (1, 1)], [(2, 0), (3, 0)], [(2, 1), (3, 1)]].

cross_partition

Chỉ có hoạt động giao tiếp giữa các phân vùng diễn ra trong mỗi nhóm quy trình. Chiến lược này lấy partition_groups – danh sách các danh sách mã nhận dạng phân vùng – và tính tích Đề các của partition_groups theo replica_ids. partition_groups phải có các phần tử duy nhất và bao gồm tất cả partition_ids. Cụ thể hơn, sử dụng cú pháp 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

Ví dụ: đối với partition_groups = [[0, 1]]num_replicas = 4, cross_partition sẽ tạo ra [[(0, 0), (0, 1)], [(1, 0), (1, 1)], [(2, 0), (2, 1)], [(3, 0), (3, 1)]].

cross_replica_and_partition

Cả hoạt động giao tiếp giữa các bản sao và giữa các phân vùng đều có thể diễn ra trong mỗi nhóm quy trình. Chiến lược này lấy replica_groups – danh sách các danh sách mã nhận dạng bản sao – và tính tích Đề các của mỗi replica_group theo partition_ids. replica_groups phải có các phần tử duy nhất và bao gồm tất cả replica_ids. Cụ thể hơn, sử dụng cú pháp 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

Ví dụ: đối với replica_groups = [[0, 1], [2, 3]]num_partitions = 2, cross_replica_and_partition sẽ tạo ra [[(0, 0), (1, 0), (0, 1), (1, 1)], [(2, 0), (3, 0), (2, 1), (3, 1)]].

flattened_ids

Chiến lược này lấy flattened_id_groups – một danh sách các danh sách mã nhận dạng quy trình "được làm phẳng" ở dạng replica_id * num_partitions + partition_id – và chuyển chúng thành mã nhận dạng quy trình. flattened_id_groups phải có các phần tử riêng biệt và bao gồm tất cả process_ids. Cụ thể hơn, sử dụng cú pháp 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

Ví dụ: đối với flattened_id_groups = [[0, 1, 2, 3], [4, 5, 6, 7]], num_replicas = 4num_partitions = 2, flattened_ids sẽ tạo ra [[(0, 0), (0, 1), (1, 0), (1, 1)], [(2, 0), (2, 1), (3, 0), (3, 1)]].

Độ chính xác

Hiện tại, StableHLO không đảm bảo độ chính xác về số học, nhưng điều này có thể thay đổi trong tương lai (#1156).

Ngữ nghĩa thực thi của phép toán được lượng tử hoá

Việc diễn giải các hoạt động StableHLO được lượng tử hoá có thể khác nhau tuỳ thuộc vào các yêu cầu và khả năng của phần cứng. Ví dụ: một số phần cứng có thể chọn diễn giải các hoạt động được lượng tử hoá bằng cách sử dụng chiến lược "giảm lượng tử hoá, thực hiện hoạt động dấu phẩy động và cuối cùng là lượng tử hoá". Những người khác có thể thực hiện toàn bộ quá trình tính toán bằng số học số nguyên. Do đó, việc diễn giải các hoạt động StableHLO được lượng tử hoá chỉ do việc triển khai cụ thể xác định. Việc diễn giải lượng tử hoá kết hợp (#1575) phải dựa trên ngữ nghĩa của nó như quy định trong thông số kỹ thuật (thông qua 1792).

Lỗi

Các chương trình StableHLO được xác thực thông qua một tập hợp lớn các ràng buộc cho từng thao tác, giúp loại bỏ nhiều loại lỗi trước thời gian chạy. Tuy nhiên, vẫn có thể xảy ra các điều kiện lỗi, chẳng hạn như do tràn số nguyên, truy cập ngoài phạm vi, v.v. Trừ phi được gọi ra một cách rõ ràng, tất cả các lỗi này đều dẫn đến hành vi do quá trình triển khai xác định, nhưng điều này có thể thay đổi trong tương lai (#1157).

Ngoại lệ dấu phẩy động

Theo ngoại lệ đối với quy tắc này, các ngoại lệ dấu phẩy động trong chương trình StableHLO có hành vi được xác định rõ. Các thao tác dẫn đến các trường hợp ngoại lệ do tiêu chuẩn IEEE-754 xác định (thao tác không hợp lệ, phép chia cho 0, tràn số, tràn số âm hoặc trường hợp ngoại lệ không chính xác) sẽ tạo ra kết quả mặc định (như được xác định trong tiêu chuẩn) và tiếp tục thực thi mà không làm tăng cờ trạng thái tương ứng; tương tự như xử lý ngoại lệ raiseNoFlag theo tiêu chuẩn. Các trường hợp ngoại lệ đối với các thao tác không theo tiêu chuẩn (ví dụ: số học phức tạp và một số hàm siêu việt nhất định) được xác định theo cách triển khai.

Hình dạng không khớp

StableHLO hỗ trợ các tensor có hình dạng động. Tuy nhiên, các hình dạng phải đồng ý tại thời gian chạy, nếu không, hành vi sẽ không xác định. StableHLO không cung cấp một thao tác rõ ràng có thể khẳng định rằng một tensor có một hình dạng nhất định tại thời gian chạy. Nhà sản xuất có trách nhiệm tạo mã chính xác.

Ví dụ cụ thể: chương trình dưới đây là hợp lệ. Tuy nhiên, trong thời gian chạy, các hình dạng chính xác của %arg0%arg1 phải giống nhau, nếu không, hành vi của chương trình sẽ không xác định:

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

Ký hiệu

Để mô tả cú pháp, tài liệu này đang sử dụng cú pháp EBNF theo ISO đã sửa đổi (ISO/IEC 14977:1996, Wikipedia), với 2 điểm sửa đổi: 1) các quy tắc được xác định bằng cách sử dụng ::= thay vì =,

2) phép nối được biểu thị bằng cách đặt cạnh nhau thay vì ,.

Để mô tả ngữ nghĩa (tức là trong các phần "Types" (Kiểu dữ liệu), "Constants" (Hằng số) và "Ops" (Thao tác)), chúng tôi đang sử dụng các công thức dựa trên cú pháp Python được mở rộng để hỗ trợ việc biểu thị ngắn gọn các thao tác trên mảng như mô tả bên dưới. Điều này phù hợp với các đoạn mã nhỏ, nhưng trong một số ít trường hợp cần các đoạn mã lớn hơn, chúng tôi sử dụng cú pháp Python thuần tuý và luôn giới thiệu rõ ràng.

Công thức

Hãy khám phá cách hoạt động của công thức dựa trên ví dụ trong quy cách dot_general. Một trong các ràng buộc cho thao tác này có dạng như sau:dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...).

Tên được dùng trong công thức này đến từ 2 nguồn: 1) hàm chung, tức là dim, 2) định nghĩa thành phần của phần tử chương trình tương ứng, tức là lhs, lhs_batching_dimensions, rhsrhs_batching_dimensions đầu vào được xác định trong phần "Đầu vào" của dot_general.

Như đã đề cập ở trên, cú pháp của công thức này dựa trên Python với một số tiện ích hướng đến sự súc tích. Để hiểu rõ công thức này, hãy chuyển đổi công thức đó thành cú pháp Python thông thường.

A) Trong các công thức này, chúng ta đang sử dụng = để biểu thị sự bình đẳng, vì vậy, bước đầu tiên để có được cú pháp Python là thay thế = bằng ==, như sau: dim(lhs, lhs_batching_dimensions...) == dim(rhs, rhs_batching_dimensions...).

B) Ngoài ra, các công thức này hỗ trợ dấu ba chấm (...) để chuyển biểu thức vô hướng thành biểu thức tensor. Nói tóm lại, f(xs...) có nghĩa là "đối với mỗi đại lượng vô hướng x trong tensor xs, hãy tính toán một đại lượng vô hướng f(x) rồi trả về tất cả các kết quả vô hướng này cùng nhau dưới dạng kết quả tensor". Trong cú pháp Python thông thường, công thức ví dụ của chúng ta sẽ chuyển thành: [dim(lhs, dim1) for dim1 in lhs_batching_dimensions] == [dim(rhs, dim2) for dim2 in rhs_batching_dimensions].

Nhờ dấu ba chấm, bạn thường có thể tránh làm việc ở cấp độ của các đại lượng vô hướng riêng lẻ. Tuy nhiên, trong một số trường hợp phức tạp, bạn có thể sử dụng cú pháp bán chính thức ở cấp thấp hơn, chẳng hạn như trong công thức start_indices[bi0, ..., :, ..., biN] trong quy cách gather. Để đảm bảo tính súc tích, chúng tôi không cung cấp một hình thức chính thức chính xác để dịch cú pháp như vậy sang Python thuần tuý, với hy vọng rằng bạn vẫn có thể hiểu một cách trực quan theo từng trường hợp. Vui lòng cho chúng tôi biết nếu bạn thấy một số công thức cụ thể khó hiểu, chúng tôi sẽ cố gắng cải thiện những công thức đó.

Ngoài ra, bạn sẽ nhận thấy rằng các công thức sử dụng dấu ba chấm để mở rộng mọi loại danh sách, bao gồm cả tensor, danh sách tensor (chẳng hạn như có thể phát sinh từ số lượng tensor có thể thay đổi), v.v. Đây là một lĩnh vực khác mà chúng tôi không cung cấp một hình thức chính thức chính xác (ví dụ: danh sách thậm chí không phải là một phần của hệ thống loại StableHLO) và thay vào đó dựa vào khả năng hiểu trực quan.

C) Phương tiện ký hiệu đáng chú ý cuối cùng mà chúng ta sử dụng là truyền tin ngầm. Mặc dù opset StableHLO không hỗ trợ truyền tin ngầm, nhưng các công thức thì có, cũng nhằm mục đích ngắn gọn. Nói tóm lại, nếu một đại lượng vô hướng được dùng trong một ngữ cảnh mà một tensor được dự kiến, thì đại lượng vô hướng sẽ được truyền đến hình dạng dự kiến.

Để tiếp tục ví dụ về dot_general, sau đây là một ràng buộc khác: 0 <= lhs_batching_dimensions < rank(lhs). Như được xác định trong quy cách dot_general, lhs_batching_dimensions là một tensor, tuy nhiên cả 0rank(lhs) đều là các đại lượng vô hướng. Sau khi chúng ta áp dụng tính năng truyền tin ngầm, công thức sẽ trở thành [0, ..., 0] <= lhs_batching_dimensions < [rank(lhs), ..., rank(lhs)].

Khi áp dụng cho một thao tác dot_general cụ thể, công thức này sẽ đánh giá thành một tensor gồm các giá trị boolean. Khi công thức được dùng làm điều kiện ràng buộc, điều kiện ràng buộc sẽ giữ nguyên nếu công thức đánh giá thành true hoặc thành một tensor chỉ có các phần tử true.

Tên

Trong công thức, phạm vi từ vựng bao gồm: 1) hàm chung, 2) định nghĩa thành viên,

3) định nghĩa cục bộ. Danh sách các hàm chung được cung cấp bên dưới. Danh sách định nghĩa về phần tử phụ thuộc vào phần tử chương trình mà ký hiệu được áp dụng:

  • Đối với các thao tác, định nghĩa thành phần bao gồm tên được giới thiệu trong các phần "Đầu vào" và "Đầu ra".
  • Đối với mọi thứ khác, định nghĩa thành phần bao gồm các phần cấu trúc của phần tử chương trình, được đặt tên theo các ký tự không kết thúc EBNF tương ứng. Hầu hết thời gian, tên của các phần cấu trúc này được lấy bằng cách chuyển đổi tên của các phần tử không kết thúc thành snake case (ví dụ: IntegerLiteral => integer_literal), nhưng đôi khi tên sẽ được rút gọn trong quá trình này (ví dụ: QuantizationStorageType => storage_type). Trong trường hợp đó, tên sẽ được giới thiệu một cách rõ ràng tương tự như các phần "Đầu vào" / "Đầu ra" trong quy cách hoạt động.
  • Ngoài ra, định nghĩa về thành viên luôn bao gồm self để tham chiếu đến phần tử chương trình tương ứng.

Giá trị

Khi được đánh giá, các công thức sẽ hoạt động với những loại giá trị sau: 1) Value (giá trị thực tế, ví dụ: dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>; các giá trị này luôn biết loại của chúng), 2) Placeholder (giá trị trong tương lai, ví dụ: lhs, rhs hoặc result; giá trị thực tế của các giá trị này chưa được biết, chỉ biết loại của chúng), 3) Type (các loại như được xác định trong phần "Loại"), 4) Function (các hàm chung như được xác định trong phần "Hàm").

Tuỳ thuộc vào ngữ cảnh, tên có thể đề cập đến các giá trị khác nhau. Cụ thể hơn, phần "Ngữ nghĩa" cho các thao tác (và các phần tương đương cho các phần tử chương trình khác) xác định logic thời gian chạy, vì vậy, tất cả các đầu vào đều có sẵn dưới dạng Value. Ngược lại, phần "Constraints" (Ràng buộc) cho các thao tác (và các thao tác tương đương) xác định logic "thời gian biên dịch", tức là một thứ thường được thực thi trước thời gian chạy, vì vậy, chỉ các đầu vào hằng số mới có sẵn dưới dạng Value và các đầu vào khác chỉ có sẵn dưới dạng Placeholder.

Tên Trong "Ngữ nghĩa" Trong phần "Constraints" (Ràng buộc)
Hàm chung Function Function
Đầu vào hằng số Value Value
Đầu vào không phải hằng số Value Placeholder
Kết quả đầu ra Value Placeholder
Định nghĩa về địa phương Tuỳ thuộc vào định nghĩa Tuỳ thuộc vào định nghĩa

Hãy xem xét một ví dụ về thao tác transpose:

%result = "stablehlo.transpose"(%operand) {
  permutation = dense<[2, 1, 0]> : tensor<3xi64>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>

Đối với thao tác này, permutation là một hằng số, vì vậy, hằng số này có sẵn dưới dạng Value trong cả ngữ nghĩa và các ràng buộc. Ngược lại, operandresult có sẵn dưới dạng Value trong ngữ nghĩa nhưng chỉ dưới dạng Placeholder trong các ràng buộc.

Hàm

Xây dựng các loại

Không có hàm nào có thể dùng để tạo các loại. Thay vào đó, chúng ta sử dụng trực tiếp cú pháp kiểu vì cú pháp này thường ngắn gọn hơn. Ví dụ: (tensor<E>, tensor<E>) -> (tensor<E>) thay vì function_type( [tensor_type([], E), tensor_type([], E)], [tensor_type([], E)]).

Hàm trên các loại

  • element_type được xác định trên các loại tensor và các loại tensor được lượng tử hoá, đồng thời trả về phần TensorElementType hoặc QuantizedTensorElementType của TensorType hoặc QuantizedTensorType tương ứng.
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 là lối tắt cho is_quantized(x) and quantization_dimension(x) is not None.

  • is_per_tensor_quantized(x: Value | Placeholder | Type) -> Value là một lối tắt cho is_quantized(x) and quantization_dimension(x) is None.

  • is_promotable(x: Type, y: Type) -> bool kiểm tra xem kiểu x có thể được chuyển đổi thành kiểu y hay không. Khi xyQuantizedTensorElementType, chương trình khuyến mãi chỉ được áp dụng cho storage_type. Phiên bản khuyến mãi cụ thể này hiện được dùng trong bối cảnh tính toán mức giảm (tham khảo RFC để biết thêm chi tiết).

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 là lối tắt cho is_quantized_tensor_element_type(x).

  • is_type_name(x: Value | Placeholder | Type) -> Value. Dùng được cho tất cả các loại. Ví dụ: is_float(x) sẽ trả về true nếu xFloatType. Nếu x là một giá trị hoặc phần giữ chỗ, thì hàm này là lối tắt cho is_type_name(type(x)).

  • max_value(x: Type) -> Value trả về giá trị tối đa của TensorElementType. Nếu x không phải là TensorElementType, thì sẽ trả về None.

  • min_value(x: Type) -> Value trả về giá trị tối thiểu có thể có của TensorElementType. Nếu x không phải là TensorElementType, thì sẽ trả về None.

  • member_name(x: Value | Placeholder | Type) -> Any. Áp dụng cho tất cả các định nghĩa thành viên member_name thuộc mọi loại. Ví dụ: tensor_element_type(x) trả về phần TensorElementType của TensorType tương ứng. Nếu x là một giá trị hoặc phần giữ chỗ, thì hàm này là lối tắt cho member_name(type(x)). Nếu x không phải là một loại có thành phần thích hợp hoặc một giá trị hoặc phần giữ chỗ của loại đó, thì sẽ trả về None.

  • is_empty_algorithm(*args: Type) kiểm tra xem tất cả các trường thuật toán dấu chấm có được đặt thành None hay không. Điều này là cần thiết vì các thuật toán dấu chấm có hành vi mặc định do quá trình triển khai xác định, nên việc chỉ định giá trị mặc định sẽ không chính xác.

Xây dựng các giá trị

  • operation_name(*xs: Value | Type) -> Value. Dành cho tất cả các hoạt động. Ví dụ: add(lhs, rhs) lấy hai giá trị tensor lhsrhs, đồng thời trả về kết quả đánh giá thao tác add với các giá trị đầu vào này. Đối với một số thao tác, ví dụ: broadcast_in_dim, các loại đầu ra của chúng là "chịu tải", tức là cần thiết để đánh giá một thao tác. Trong trường hợp này, hàm sẽ lấy các loại này làm đối số.

Hàm trên các giá trị

  • Bạn có thể sử dụng tất cả các toán tử và hàm của Python. Ví dụ: cả ký hiệu subscriptionslicing của Python đều có sẵn để lập chỉ mục vào các tensor, tensor được lượng tử hoá và các bộ dữ liệu.

  • to_destination_type(x: Value, destination_type: Type) -> Value được xác định trên các tensor và trả về giá trị đã chuyển đổi của x dựa trên type(x)destination_type như sau:

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)

Có cuộc thảo luận ban đầu về việc hợp nhất các thao tác convert, uniform_quantizeuniform_dequantize (#1576). Sau khi hợp nhất, chúng ta không cần hàm trên và có thể sử dụng tên thao tác cho convert.

  • is_nan(x: Value) -> Value được xác định trên các tensor và trả về true nếu tất cả các phần tử của xNaN hoặc false. Nếu x không phải là một tensor, thì hàm này sẽ trả về None.

  • is_sorted(x: Value) -> Value được xác định trên các tensor và trả về true nếu các phần tử của x được sắp xếp theo thứ tự tăng dần đối với thứ tự từ vựng tăng dần của chỉ mục hoặc false của chúng. Nếu x không phải là một tensor, hàm sẽ trả về None.

  • is_unique(x: Value) -> Value được xác định trên các tensor và trả về true nếu x không có các phần tử trùng lặp hoặc false nếu không. Nếu x không phải là một tensor, thì hàm này sẽ trả về None.

  • member_name(x: Value) -> Any được xác định cho tất cả các định nghĩa thành viên member_name của tất cả các giá trị. Ví dụ: real_part(x) trả về phần RealPart của ComplexConstant tương ứng. Nếu x không phải là giá trị có thành phần thích hợp, hàm sẽ trả về None.

  • same(x: Value) -> Value được xác định trên các tensor và trả về true nếu các phần tử của x đều bằng nhau hoặc false nếu không. Nếu tensor không có phần tử, thì điều đó được tính là "tất cả đều bằng nhau", tức là hàm trả về true. Nếu x không phải là một tensor, thì hàm này sẽ trả về None.

  • split(x: Value, num_results: Value, axis: Value) -> Value được xác định trên các tensor và trả về các lát num_results của x dọc theo trục axis. Nếu x không phải là một tensor hoặc dim(x, axis) % num_results != 0, thì hàm này sẽ trả về None.

  • is_defined_in_parent_scope(x: Value) -> Value được xác định trên các chuỗi và trả về true nếu x là tên của một hàm được xác định trong cùng phạm vi với hàm mẹ của thao tác có liên quan.

  • is_namespaced_op_name(x: Value) -> Value được xác định trên các chuỗi và trả về true nếu x là tên thao tác hợp lệ, tức là tên đó tuân theo biểu thức chính quy sau: [a-zA-Z][a-zA-Z0-9_]*([.][a-zA-Z0-9_$]+)+

Tính toán hình dạng

  • axes(x: Value | Placeholder | Type) -> Value là lối tắt cho range(rank(x)).

  • dim(x: Value | Placeholder | Type, axis: Value) -> Value là lối tắt cho shape(x)[axis].

  • dims(x: Value | Placeholder | Type, axes: List) -> List là lối tắt cho list(map(lambda axis: dim(x, axis), axes)).

  • index_space(x: Value | Placeholder | Type) -> Value được xác định trên các tensor và trả về chỉ mục size(x) cho TensorType tương ứng được sắp xếp theo thứ tự từ điển tăng dần, tức là [0, ..., 0], [0, ..., 1], ..., shape(x) - 1. Nếu x không phải là một loại tensor, loại tensor được lượng tử hoá hoặc một giá trị hay một phần giữ chỗ của một trong các loại này, thì hàm sẽ trả về None.

  • rank(x: Value | Placeholder | Type) -> Value là lối tắt cho size(shape(x)).

  • shape(x: Value | Placeholder | Type) -> Value được xác định trong phần "Hàm trên các loại" thông qua member_name.

  • size(x: Value | Placeholder | Type) -> Value là lối tắt cho reduce(lambda x, y: x * y, shape(x)).

Phép tính lượng tử hoá

  • def baseline_element_type(x: Value | Placeholder | Type) -> Type là một lối tắt cho element_type(baseline_type(x)).

  • baseline_type được xác định trên các loại tensor và các loại tensor được lượng tử hoá, đồng thời chuyển đổi chúng thành một "đường cơ sở", tức là một loại có cùng hình dạng nhưng các thông số lượng tử hoá của loại phần tử được đặt lại thành các giá trị mặc định. Đây là một thủ thuật hữu ích để so sánh cả hai loại tensor và tensor được định lượng một cách đồng nhất, điều này thường cần thiết. Đối với các loại được lượng tử hoá, điều này cho phép so sánh các loại bỏ qua các tham số lượng tử hoá, tức là shape, storage_type, expressed_type, storage_min, storage_maxquantization_dimension (đối với loại được lượng tử hoá theo trục) phải khớp với nhau, nhưng scaleszero points có thể khác nhau.

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 được xác định trên các loại tensor được lượng tử hoá và chuyển chúng thành các loại tensor dấu phẩy động. Điều này xảy ra thông qua việc chuyển đổi các phần tử được lượng tử hoá (biểu thị các giá trị số nguyên của loại bộ nhớ) thành các giá trị dấu phẩy động tương ứng của loại được biểu thị bằng cách sử dụng điểm 0 và tỷ lệ liên kết với loại phần tử được lượng tử hoá.
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 được xác định trên các loại tensor dấu phẩy động và chuyển chúng thành các loại tensor được lượng tử hoá. Điều này xảy ra thông qua việc chuyển đổi các giá trị dấu phẩy động của loại được biểu thị thành các giá trị số nguyên tương ứng của loại lưu trữ bằng cách sử dụng điểm không và tỷ lệ liên kết với loại phần tử được lượng tử hoá.
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 được dùng để chỉ định các phép tính theo phần tử trên các tensor được lượng tử hoá. Thao tác này sẽ khử lượng tử hoá, tức là chuyển các phần tử được lượng tử hoá thành các loại được biểu thị, sau đó thực hiện một thao tác rồi lượng tử hoá, tức là chuyển kết quả trở lại các loại lưu trữ. Hiện tại, hàm này chỉ hoạt động cho quá trình định lượng trên mỗi tensor. Lượng tử hoá theo trục đang trong quá trình phát triển (#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 được dùng để chỉ định chỉ định lượng chỉ trọng số cho thao tác kết hợp chấp nhận lhs ở dấu phẩy động và rhs ở các loại được định lượng. Thao tác này giải lượng tử hoá các đầu vào được lượng tử hoá thành các loại được biểu thị và thực hiện tính toán ở dạng số thực. Loại phần tử của tensor lhs có dấu phẩy động và loại được biểu thị của tensor rhs được lượng tử hoá phải giống nhau.
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))

Tính toán lưới

  • cross_partition(replica_groups: Value) -> Value. Xem phần "cross_replica" ở trên.

  • cross_replica(replica_groups: Value) -> Value. Xem phần "cross_replica" ở trên.

  • cross_replica_and_partition(replica_groups: Value) -> Value. Xem phần "cross_replica_and_partition" ở trên.

  • flattened_ids(replica_groups: Value) -> Value. Xem phần "flattened_ids" ở trên.

Tính năng động

Các giá trị StableHLO có thể có kích thước phương diện linh động, ví dụ: tensor<?xi64>. Tuy nhiên, các giá trị StableHLO không thể có số lượng phương diện động (tính linh hoạt không được xếp hạng, ví dụ: tensor<*xi64>). Các toán hạng và kết quả được phép sử dụng kích thước phương diện động, ngay cả khi có các ràng buộc về kích thước. Các ràng buộc sẽ được xác minh tĩnh nếu có thể, nếu không, chúng sẽ được hoãn lại đến thời gian chạy và các trường hợp không khớp sẽ dẫn đến hành vi không xác định. Hãy xem ví dụ bên dưới.

Hình dạng không khớp cho các phép toán đơn nguyên theo phần tử

Hãy xem xét chương trình đồ chơi sau:

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

Chương trình như vậy là không bình thường, vì không phổ biến khi biết hình dạng của kết quả nhưng không biết hình dạng của đầu vào. Tuy nhiên, đây là một chương trình StableHLO hợp lệ. Không thể xác thực tĩnh hoạt động abs trong chương trình này vì không biết chính xác hình dạng của toán hạng. Tuy nhiên, các hình dạng chắc chắn tương thích và bạn có thể kiểm tra tĩnh điều này: ? có thể trở thành 2 trong thời gian chạy và sẽ không có vấn đề gì. Tuy nhiên, ? cũng có thể là một số nguyên khác, trong trường hợp đó, hành vi sẽ không xác định.

Lưu ý rằng nếu kích thước phương diện là động trong kết quả, thì không thể có hành vi không xác định. Thật vậy, không có kích thước "dự kiến" nên không thể có sự không khớp.

Hình dạng không khớp cho các thao tác nhị phân theo phần tử

Hãy xem xét chương trình đồ chơi sau:

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

Đối với các thao tác nhị phân theo phần tử, hình dạng của dữ liệu đầu vào và kết quả phải khớp với nhau trong thời gian chạy. Tại thời gian biên dịch, các phương diện tĩnh phải bằng nhau, nếu không, chúng chỉ cần tương thích. Nếu bất kỳ phương diện nào là động trong các đầu vào, thì có thể có hành vi không xác định tại thời gian chạy, vì kích thước động có thể không khớp với kích thước tương ứng trong toán hạng khác (cho dù đó là kích thước tĩnh hay động). Nếu tất cả các đầu vào đều tĩnh, thì việc kết quả có động hay không không quan trọng: các phương diện đã biết tĩnh sẽ được kiểm tra tĩnh và các phương diện động không áp đặt bất kỳ ràng buộc nào.

Hình dạng không khớp cho các thao tác lấy hình dạng đầu ra làm toán hạng

Hãy xem xét chương trình đồ chơi sau:

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

Các giá trị trong toán hạng hình dạng tại thời gian chạy phải khớp với hình dạng của kết quả, nếu không, hành vi sẽ không xác định. Tức là trong thời gian chạy, %arg0 phải có giá trị là dense<[3, 4]> : tensor<2xi32>. Nếu toán hạng hình dạng là hằng số, thì điều này có thể được xác minh một cách tĩnh. Nếu hình dạng kết quả hoàn toàn linh hoạt, thì không thể có sự không khớp.