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

StableHLO là một tập hợp thao tác dành cho các thao tác cấp cao (HLO) trong mô hình học máy (ML). StableHLO hoạt động như một lớp khả năng di chuyển giữa các khung ML và trình biên dịch ML khác nhau: các khung ML tạo ra chương trình StableHLO tương thích với các trình biên dịch ML sử dụng 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 công nghệ học máy bằng cách tăng khả năng tương tác giữa nhiều khung học máy (chẳng hạn như TensorFlow, JAX và PyTorch) và các trình biên dịch học máy (như XLA và IREE). Để đạt được mục tiêu đó, tài liệu này cung cấp thông số kỹ thuật cho ngôn ngữ lập trình StableHLO.

Quy cách này bao gồm 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 ổn định HLO, trong đó bao gồm các hàm ổn định HLO, trong đó có các hoạt động ổn định HLO. Trong cấu trúc đó, phần Ops (Thao tác) 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 thực thi cùng nhau trong một chương trình. Cuối cùng, phần Ký hiệu thảo luận về ký hiệu được sử dụng trong toàn bộ quy cách.

Để xem thông số kỹ thuật của bản phát hành StableHLO trước đây, 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ụ: Thông số kỹ thuật StableHLO v0.19.0. Để xem các thay đổi xảy ra ở mỗi lần nâng cấp phiên bản nhỏ của StableHLO, hãy tham khảo nhật ký phiên bản trong VhloDialect.td.

Chương trình

Program ::= {Func}

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

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}

Hàm StableHLO (còn gọi là hàm được đặt tên) có một giá trị nhận dạng, dữ liệu đầu vào/đầu ra và 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 ổn địnhHLO tương tự như giá trị nhận dạng trong nhiều ngôn ngữ lập trình, với hai điểm đặc biệt: 1) tất cả giá trị nhận dạng đều có các dấu hiệu phân biệt nhiều loại giá trị nhận dạng, 2) giá trị nhận dạng giá trị 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
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 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ử chương trình khác. Các loại StableHLO tương tự như các loại trong nhiều ngôn ngữ lập trình, với đặc điểm 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à loại giá trị).

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

Loại tensor biểu thị các tensor, tức là các mảng nhiều chiều. Các thành phần này có một hình dạng và một loại phần tử, trong đó hình dạng đại diện cho kích thước kích thước không âm hoặc không xác định theo thứ tự tăng dần của kích thước tương ứng (còn 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ó hai phương diện (hay nói cách khác là hai 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 một phần hoặc hoàn toàn (động), ví dụ: tensor<?x2xf64> không xác định một phần và tensor<?x?xf64> không xác định hoàn toàn. Kích thước kích thước động được biểu thị bằng ?. Không thể xếp hạng hình dạng.

Trong tương lai, chúng tôi dự định mở rộng các loại tensor ngoài kích thước và loại phần tử, chẳng hạn như bao gồm cả 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ố 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ố đại lượng hằng số dấu phẩy động (C4-C6), (C9), (C10), (C13)
zero_points số lượng biến của hằng số số nguyên (C7-C9)

Các loại phần tử được lượng tử hoá biểu thị các giá trị số nguyên của một loại bộ nhớ trong khoảng 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 đã biểu thị. Đối với một giá trị số nguyên nhất định i, giá trị dấu phẩy động tương ứng f có thể được tính là f = (i - zero_point) * scale, trong đó scalezero_point được gọi là tham số lượng tử hoá. storage_minstorage_max là không bắt buộc trong ngữ pháp, nhưng 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ó các điều kiện 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 là 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ó rất 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ố và độ lệch. Chúng tôi dự định sẽ khám phá vấn đề này trong tương lai gần (#1404).

Chúng tôi đang thảo luận về ngữ nghĩa của QuantizationZeroPoint, bao gồm cả loại, giá trị và liệu có thể chỉ có một hoặ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, thông số kỹ thuật xung quanh điểm 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ó áp đặt bất kỳ quy tắc ràng buộc nào đối với các giá trị này và đối với các giá trị của tensor lượng tử hoá hay không (#1406).

Cuối cùng, chúng tôi dự định khám phá cách thể hiện các điểm không xác định và điểm 0, tương tự như cách chúng tôi dự định khám phá cách thể hiện 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 giống hệt như tensor thông thường, ngoại trừ việc các phần tử của chúng có loại phần tử lượng tử hoá thay vì kiểu phần tử thông thường.

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

  • Đối với việc lượng tử hoá theo từng tensor:
    • Không có quy tắc ràng buộc nào khác.
  • Đối với việc lượng tử hoá theo 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 đại diện cho mã thông báo, tức là các giá trị mờ do một số thao tác tạo ra 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}]

Loại dữ liệu tuple đại diện cho các tuple, tức là các danh sách không đồng nhất. Bộ dữ liệu 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ộ dữ liệu được dùng để biểu thị dữ liệu đầu vào và đầu ra biến đổi. Trong StableHLO, đầu vào và đầu ra biến đổi được hỗ trợ nguyên bản và việc sử dụng duy nhất các bộ dữ liệu trong StableHLO là để biểu thị toàn diện ABI HLO, trong đó, ví dụ: T, tuple<T>tuple<tuple<T>> có thể khác nhau đáng kể tuỳ thuộc vào cách triển khai cụ thể. Trong tương lai, chúng tôi dự định thực hiện các thay đổi đối với HLO ABI để có thể xoá các loại bộ dữ liệu từ 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'

Loại phần tử đại diện cho các phần tử của 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à lớp đầu tiên 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ị của các loại này (do đó, 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 đại diện cho các giá trị boolean truefalse.
  • Loại số nguyên có thể có dấu (si) hoặc không dấu (ui) và có một trong các chiều rộng bit được hỗ trợ (2, 4, 8, 16, 32 hoặc 64). Loại siN đã ký biểu thị giá trị số nguyên từ -2^(N-1) đến 2^(N-1)-1, và loại uiN không dấu biểu thị giá trị số nguyên từ 0 đến 2^N-1.
  • Loại dấu phẩy động có thể là một trong những loại sau:
  • Loại phức đại diện cho các giá trị phức có phần thựcphần ảo thuộc cùng một loại 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}]

Loại hàm đại diện cho cả hàm được đặt tên và hàm ẩn danh. Các hàm này có loại đầu vào (danh sách các loại ở bên trái ->) và loại đầu ra (danh sách các loại ở bên phải ->). Trong nhiều ngôn ngữ lập trình, các loại hàm là lớp đầu tiên, nhưng không phải trong StableHLO.

StringType ::= 'string'

Loại chuỗi biểu thị 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à lớp đầu tiên 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

Toán tử StableHLO (còn gọi là toán tử) đại diện cho một tập hợp kín các toán tử cấp cao trong mô hình học máy. Như đã thảo luận ở trên, cú pháp StableHLO được lấy cảm hứng rất nhiều từ MLIR. Đây không nhất thiết là giải pháp thay thế phù hợp nhất về mặt công thái học, nhưng có thể là giải pháp 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 máy học và trình biên dịch máy học.

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

Thao tác StableHLO (còn gọi là ops) có tên, đầu vào/đầu ra và chữ ký. Tên này bao gồm tiền tố stablehlo. và một ký tự ghi nhớ giúp xác định duy nhất một trong các hoạt động được hỗ trợ. Hãy xem phần bên dưới để biết danh sách đầy đủ tất cả hoạt động đượ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 dữ liệu đầu vào và tạo dữ liệu đầu ra. Các giá trị đầu vào được phân loại thành giá trị đầu vào (được tính trong quá trình thực thi), hàm đầu vào (được cung cấp tĩnh vì trong các hàm StableHLO không phải là giá trị hạng nhất) và các thuộc tính đầu vào (cũng được cung cấp theo phương thức tĩnh). Loại dữ liệu đầu vào và đầu ra mà một toán tử tiêu thụ và tạo ra phụ thuộc vào ký hiệu của toán tử đó. Ví dụ: toán tử add sử dụng 2 giá trị đầu vào và tạo ra 1 giá trị đầu ra. So sánh, toán tử select_and_scatter 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 "anonymous"), 2) chúng không khai báo loại đầu ra (loại đầu ra được suy ra từ hoạt động 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 được sử dụng (xem quá trình tạo 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" hoạt động được kết nối với nhau thông qua hoạt động nhảy. Các khối này có mã nhận dạng tương ứng với quá trình tạo Unused để có thể phân biệt với nhau. StableHLO không có hoạt động nhảy, vì vậy, phần tương ứng của cú pháp MLIR không được sử dụng (nhưng vẫn ở đó).

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à phương thức chính để chỉ định siêu dữ liệu tĩnh cho các phần tử của chương trình. Ví dụ: toán tử concatenate sử dụng thuộc tính dimension để chỉ định phương diện theo đó các giá trị đầu vào của toán tử này được nối với nhau. Tương tự, toán tử slice sử dụng nhiều thuộc tính như start_indiceslimit_indices để chỉ định các giới hạn dùng để cắt giá trị đầu vào.

Hiện tại, các chương trình StableHLO trong thực tế đô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 đưa các thuộc tính này vào cơ chế ổn định HLO hoặc cấm các thuộc tính này xuất hiện trong các chương trình ổn định HLO. Trong thời gian chờ đợi, sau đây là danh sách các thuộc tính nà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ý toán tử bao gồm các loại của tất cả giá trị đầu vào (danh sách các loại ở 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 ở bên phải của ->). Nói một cách nghiêm ngặt, các loại đầu vào là thừa và các loại đầu ra hầu như luôn thừa (vì đối với hầu hết các toán tử StableHLO, các loại đầu ra có thể được suy ra từ các đầu vào). Tuy nhiên, chữ ký op được cố ý đưa vào cú pháp StableHLO để tương thích với MLIR.

Dưới đây là một ví dụ về toán tử có ký hiệu là select_and_scatter. Hàm 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 toán tử chỉ bao gồm các loại giá trị đầu vào (nhưng không phải các loại hàm đầu vào và thuộc tính được cung cấp cùng dòng).

%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ố ổn địnhHLO có một giá trị cố định và một kiểu cùng biểu thị một giá trị StableHLO. Nhìn chung, kiểu là một phần của cú pháp hằng số, trừ phi nó không rõ ràng (ví dụ: hằng số boolean rõ ràng có kiểu i1, trong khi một hằng số nguyên có thể có nhiều kiểu).

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ố số nguyên biểu thị các giá trị số 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, ví dụ như nhị phân hoặc bát phân, không được hỗ trợ. Hằng số số nguyên có các quy tắ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 ký hiệu khoa học. Ngoài ra, bạn có thể sử 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 quy tắc ràng buộc sau:

  • (C1) Nếu sử dụng ký hiệu không phải hệ thập lục phân, is_wellformed(float_literal, float_type).
  • (C2) Nếu sử dụng ký hiệu thập lục phân, 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 phần thực (đến trước) và phần ảo (đến sau). Ví dụ: (1.0, 0.0) : complex<f32> đại diện cho 1.0 + 0.0i(0.0, 1.0) : complex<f32> đại diện cho 0.0 + 1.0i. Thứ tự lưu trữ các phần này trong bộ nhớ được xác định bằng cách triển khai. Hằng số phức có các quy tắ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ý hiệu NumPy. Ví dụ: dense<[[1, 2, 3], [4, 5, 6]]> : tensor<2x3xi32> đại diện cho một giá trị tensor với ánh xạ sau đây từ chỉ mục đến phần tử: {0, 0} => 1, {0, 1} => 2, {0, 2} => 3, {1, 0} => 4, {1, 1} => 5, {1, 2} => 6. Việc triển khai sẽ xác định thứ tự lưu trữ các phần tử này trong bộ nhớ. Hằng số tensor có các quy tắ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, false.
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral  ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'

Hằng số tensor lượng tử hoá biểu thị các giá trị tensor 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 kiểu lưu trữ. Hằng số tensor lượng tử hoá có các quy tắ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 cố định bao gồm các byte được chỉ định bằng ký tự ASCII và trình tự thoát. Các byte này không phụ thuộc vào phương thức mã hoá, vì vậy, việc diễn giải các byte này là do phương thức triển khai xác định. Hằng chuỗi có kiểu string.

Hoạt động

abs

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 thực hiện các thao tác 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 số 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 kiểu phức hoặc tensor lượng tử hoá mỗi tensor (C1-C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor của loại số nguyên có dấu hoặc dấu phẩy động hoặc tensor lượng tử hoá 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).
    • 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 boolean: logic OR.
  • Đối với số nguyên: phép cộng số nguyên.
  • Đối với số thực độ chính xác đơn: addition từ IEEE-754.
  • Đối với số phức: phép cộng số phức.
  • Đối với các kiểu 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 lượng tử hoá (C1-C6)
(I2) rhs tensor hoặc tensor 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 lượng tử hoá (C1-C7)

Giới hạn

  • Nếu toán tử sử dụng tensor không được lượng tử hoá:
    • (C1) type(lhs) = type(rhs) = type(result).
  • Nếu phép toán sử dụng tensor lượng tử hoá:
    • (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 là 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]]

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ả, 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 biến của token

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 ở 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 tensor results.

Thao tác này chia lưới quy trình StableHLO thành process_groups được định nghĩa 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]]

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 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 tensor results.

Thao tác này chia lưới quy trình StableHLO thành process_groups được định nghĩa 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 được xác định bằng cách triển khai, trong đó hoạt động duyệt theo thứ 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ố đại lượng 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 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 tensor results. Thao tác này chia lưới quy trình StableHLO thành process_groups được định nghĩa 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] trong đó 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ố 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 dùng cross_replica.
    • num_partitions nếu 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ừ, nếu 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 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 boolean: AND logic.
  • Đối với số nguyên: thao tác bit 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 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: 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)
(I2) rhs tensor dấu phẩy động hoặc loại 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 dấu phẩy động hoặc loại 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. Chính thức hơn, phép toán này có thể được biểu thị dưới dạng một phép phân ly cho các phép toán 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 lượng tử hoá, 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 lượng tử hoá trên mỗi tensor (C1-C3), (C5)
(I2) scale Tensor 1 chiều của loại dấu phẩy động hoặc lượng tử hoá theo tensor (C2), (C4), (C5)
(I3) mean Tensor 1 chiều của loại lượng tử hoá theo dấu phẩy động hoặc mỗi tensor (C2), (C4)
(I4) variance Tensor 1 chiều của loại dấu phẩy động hoặc lượng tử hoá theo tensor (C2), (C4)
(I5) grad_output tensor thuộc loại dấu phẩy động hoặc tensor 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 loại dấu phẩy động hoặc tensor lượng tử hoá mỗi tensor (C2), (C3)
grad_scale Tensor 1 chiều của loại dấu phẩy động hoặc lượng tử hoá theo tensor (C2), (C4)
grad_offset Tensor 1 chiều của loại dấu phẩy động hoặc lượng tử hoá theo 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 quá trình phân ly cho 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 lượng tử hoá, 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 loại dấu phẩy động hoặc tensor lượng tử hoá mỗi tensor (C1-C7)
(I2) scale Tensor 1 chiều của loại dấu phẩy động hoặc lượng tử hoá theo tensor (C2), (C3)
(I3) offset Tensor 1 chiều của loại lượng tử hoá theo dấu phẩy động hoặc mỗi tensor (C2), (C4)
(I4) mean Tensor 1 chiều của loại dấu phẩy động hoặc lượng tử hoá theo tensor (C5)
(I5) variance Tensor 1 chiều của loại dấu phẩy động hoặc lượng tử hoá theo 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 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, đồng thời chuẩn hoá tensor operand tạo ra tensor output, batch_meanbatch_var. Chính thức hơn, thao tác này có thể được biểu thị dưới dạng quá trình phân ly cho các thao tác ổn định hiện có bằ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 lượng tử hoá, 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 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 lượng tử hoá theo tensor (C2), (C3)
(I3) offset Tensor 1 chiều của dấu phẩy động hoặc lượng tử hoá theo 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 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 lượng tử hoá theo tensor (C2), (C5)
batch_var Tensor 1 chiều của dấu phẩy động hoặc lượng tử hoá theo 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 thao tác truyền bit trên tensor operand và tạo tensor result trong đó các bit của toàn bộ tensor operand được diễn giải lại bằng cách sử dụng loại tensor result.

Chính thức hơn, với E = element_type(operand), E' = element_type(result)R = rank(operand):

  • Nếu là num_bits(E') < num_bits(E), bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1]).
  • Nếu là 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ề giá trị đại diện 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 bằng cách triển khai vì cách trình bày chính xác của tensor được xác định bằng cách triển khai và cách trình bày chính xác của các loại phần tử cũng được xác định bằng 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 lượng tử hoá (C1-C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor 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 giá trị là 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 là 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

Ví dụ khác

broadcast_in_dim

Ngữ nghĩa

Mở rộng các 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. Chính thức hơn, result[result_index] = operand[operand_index] trong đó đối với tất cả d trong axes(operand):

  • operand_index[d] = 0 nếu dim(operand, d) = 1.
  • 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 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 lượng tử hoá (C1), (C3), (C5-C6)

Giới hạn

  • (C1) element_type(result) được xác định bằng:
    • 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ả d trong axes(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ả 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. Chính thức hơn là result = selected_branch(), trong đó:

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

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 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 tính căn bậc ba 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ố thực độ chính xác đơn: rootn(x, 3) từ IEEE-754.
  • Đối với số phức: căn bậc ba phức.
  • Đối với các loại được lượng tử hoá: 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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 số nguyên trên của tensor operand theo từng phần tử và tạo ra tensor result. Triển khai phép toán roundToIntegralTowardPositive theo thông số kỹ thuật IEEE-754. Đối với các loại được lượng tử hoá, 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 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 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ép phân ly Cholesky của một loạt ma trận.

Chính thức hơn, đối với tất cả i trong index_space(result), result[i0, ..., iR-3, :, :] là một phép phân ly Cholesky của a[i0, ..., iR-3, :, :], ở dạng ma trận tam giác dưới (nếu lowertrue) hoặc 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 bằng cách triển khai.

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

Đối với các loại được lượng tử hoá, 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1-C3)
(I2) lower Hằng số tensor 0 chiều thuộc loại i1

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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

Giới hạn mọi phần tử của tensor operand giữa giá trị tối thiểu và tối đa và tạo ra tensor result. Chính thức hơn là 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á, thực hiện dequantize_op_quantize(clamp, min, operand, max, type(result)).

Việc áp đặt thứ tự trên số phức 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á tính năng hỗ trợ số phức cho toán tử 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á theo tensor (C1), (C3)
(I2) operand tensor hoặc tensor lượng tử hoá theo tensor (C1–C4)
(I3) max tensor hoặc tensor lượng tử hoá 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á 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 mục tiêu và tạo một tensor result.

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

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

Sau đó, result@process được xác định bằng:

  • operand@process_groups[i, 0] nếu tồn tại một i sao cho quá 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 lượng tử hoá theo tensor (C3)
(I2) replica_groups số lượng biến 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 lượng tử hoá theo 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 mục tiêu và tạo một tensor result.

Thao tác này chia lưới quy trình StableHLO thành process_groups được định nghĩa 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 xác định bằng:

  • 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á theo 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á 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 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]]

Ví dụ khác

so sánh

Ngữ nghĩa

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

Các 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, toán tử này sẽ triển khai các phép toán IEEE-754 sau:

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

Đối với các loại phần tử dấu phẩy động có compare_type = TOTALORDER, phương thức này sử dụng tổ hợp toán tử totalOrdercompareQuietEqual trong IEEE-754.

Đối với các loại phần tử phức tạp, việc so sánh theo thứ tự bảng chữ cái của 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 thứ tự trên 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á tính năng 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 lượng tử hoá. 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 lượng tử hoá theo tensor (C1–C3)
(I2) rhs tensor hoặc tensor lượng tử hoá theo 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 chuyển đổi theo phần tử thành một giá trị phức từ một cặp giá trị thực và giá trị ảo, lhsrhs, đồng thời tạo ra một 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 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)]

Ví dụ khác

tổng hợp

Ngữ nghĩa

Đóng gói một phép toán được tạo thành (thành phần) từ các phép toán StableHLO khác, lấy inputscomposite_attributes và tạo ra results. Ngữ nghĩa của hoạt động được triển khai bằng thuộc tính decomposition. Bạn có thể thay thế hoạt động composite bằng quá trình phân rã 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 ly không cung cấp cùng một ngữ nghĩa toán tử, hãy ưu tiên sử dụng custom_call.

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

Thông tin đầu vào

Hãng nhạc Tên Loại
(I1) inputs số lượng giá trị biến thiên
(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ố 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ị biến

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 chiều dimension theo cùng thứ tự như các đối số đã cho và tạo ra một tensor result. 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 phương diệ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á theo 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]]

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 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 chuyển đổi phần tử thông minh từ loại phần tử này sang loại phần tử khác trên tensor operand và tạo ra tensor result.

Đối với các lượt chuyển đổi boolean-to-any-supported-type (boolean sang bất kỳ loại nào được hỗ trợ), giá trị false được chuyển đổi thành 0 và giá trị true được chuyển đổi thành 1. Đối với lượt chuyển đổi any-supported-type-to-boolean, một giá trị bằng 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 phần bên dưới để biết cách hoạt động của tính nă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 dấu phẩy động sang số nguyên, phần thập phân sẽ bị cắt bớt. Nếu không thể biểu thị giá trị bị cắt bớt trong loại đích, thì hành vi sẽ là TBD (#180).

Việc chuyển đổi liên quan đến số phức sang số phức tuân theo hành vi tương tự của các lượt chuyển đổi dấu phẩy động sang dấu phẩy động để chuyển đổi phần thực và phần ảo.

Đối với các phép chuyển đổi complex-to-any-other-type (phức tạp sang bất kỳ loại nào khác) và any-other-type-to-complex (bất kỳ loại nào khác sang phức tạp), giá trị ảo nguồn sẽ bị bỏ qua hoặc giá trị ảo đích sẽ được đặt thành 0. Quá trình chuyển đổi phần thực tuân theo các phép chuyển đổi dấu phẩy động.

Về nguyên tắc, toán tử này có thể biểu thị việc giải lượng tử hoá (chuyển đổi từ tensor lượng tử hoá sang tensor thông thường), lượng tử hoá (chuyển đổi từ tensor thông thường sang tensor lượng tử hoá) và lượng tử hoá lại (chuyển đổi giữa các tensor lượng tử hoá), nhưng hiện tại, chúng ta có các toán tử 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 toán tử này có thể được hợp nhất vào 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)]

Ví dụ khác

tích chập

Ngữ nghĩa

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

tích chập

Một cách chính thức hơn, hãy cân nhắc việc điều chỉnh lại khung hình của các dữ liệu đầu vào sau đây theo lhs để có thể hiện các cửa sổ của 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).

Quá trình định khung lại 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] trong đó 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 sử dụng nên trong tương lai, chúng tôi sẽ dự định loại bỏ 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 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 lượng tử hoá mỗi tensor (C1), (C10-C11), (C14) (C25), (C27-C28), (C31-C32), (C34)
(I2) rhs tensor hoặc tensor 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ố 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 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) Cho 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 trước 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 toán tử sử dụng tensor không được lượng tử hoá:
    • (C27) element_type(lhs) = element_type(rhs) = element_type(result).
  • Nếu thao tác sử dụng tensor lượng tử hoá:
    • (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 là 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 giá trị là !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]]
//          ]]

Ví dụ khác

cosin

Ngữ nghĩa

Thực hiện phép toán cosin 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 thực hiện các thao tác 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 kiểu 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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]]

Ví dụ khác

count_leading_zeros

Ngữ nghĩa

Thực hiện đếm số lượng bit số 0 ở đầu theo phần tử trong tensor operand và tạo ra một tensor 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 triển khai xác định call_target_name, thao tác này sẽ lấy inputscalled_computations và tạo ra results. Bạn có thể dùng has_side_effect, backend_configapi_version để cung cấp thêm siêu dữ liệu do phương thức triển khai xác định.

Hiện tại, toán tử 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 hữu cơ của toán tử tương ứng trong trình biên dịch XLA. Trong tương lai, chúng tôi dự định 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ị biến thiên
(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 biến số của hằng số thuộc loại string

Kết quả đầu ra

Tên Loại
results số lượng giá trị biến thiên

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>

chia

Ngữ nghĩa

Thực hiện phép chia từng phần tử của tensor lhs bị chia và số chia rhs, đồ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ố nguyên: phép chia số nguyên tạo ra thương đại số với mọi phần phân số 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 số nguyên, dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)
(I2) rhs tensor số nguyên, dấu phẩy động hoặc loại 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 của số nguyên, dấu phẩy động hoặc kiểu phức hoặc tensor lượng tử hoá 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]

Ví dụ khác

dot_general

Ngữ nghĩa

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

Chính thức 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 kiểu lượng tử hoá kết hợp, 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 phần phụ trợ của trình 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õ, nhưng chúng tôi dự định sẽ giải quyết vấn đề này trong #755):

  • DEFAULT: Tính toán nhanh nhất, nhưng có độ chính xác gần đúng nhất với số ban đầu.
  • HIGH: Tính toán chậm hơn, nhưng gần đúng hơn với số ban đầu.
  • HIGHEST: Phương thức tính toán chậm nhất, nhưng có độ chính xác gần 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 tính dấu chấm, đồng thời xác định độ chính xác. Nếu bạn đặt các trường thuộc tính của thuật toán, thì precision_config phải là DEFAULT. DotAlgorithms không có giá trị mặc định, vì các tham số mặc định được xác định trong quá trình triển khai. Do đó, tất cả các trường thuật toán dấu chấm có thể được đặt 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à bên trái và bên phải của phép toán được làm tròn. Các loại độ chính xác độc lập với 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 thực hiện 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 dấu chấm "gốc" trên các giá trị đó – thường để mô phỏng độ chính xác cao hơn (ví dụ: Tận dụng Loại dữ liệu trí tuệ nhân tạo bfloat16 để tính toán độ chính xác cao hơn: bf16_6x tf). Đối với các thuật toán không có quá trình phân ly, 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ỹ ở độ chính xác thấp hơn trong một số bước hay không (ví dụ: CUBLASLT_MATMUL_DESC_FAST_ACCUM).

Ví dụ về 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}

Việc quyết định tổ hợp nào được hỗ trợ là tuỳ thuộc vào cách triển khai. Nhìn chung, không đảm bảo rằng mỗi thuật toán đều được người dùng StableHLO hỗ trợ trên mỗi 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ì lỗi sẽ được báo cáo thay vì quay lại một phương án thay thế. Tính năng xác minh StableHLO sẽ cung cấp tính năng xác minh tốt nhất, 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 hỗ trợ #2483 ghi lại kế hoạch tạo tài liệu tập trung về các thuật toán được hỗ trợ bởi 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 lượng tử hoá theo tensor (C5-C6), (C9-C10), (C12-C14), (C17-C18), (C20)
(I2) rhs tensor hoặc tensor 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 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 toán tử sử dụng tensor không được lượng tử hoá:
    • (C13) element_type(lhs) = element_type(rhs).
  • Nếu phép toán sử dụng tensor lượng tử hoá:
    • (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 có 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 giá trị là !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

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

Toán tử 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, tất 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 lượng tử hoá (C1-C2), (C5-C6), (C9)
(I2) output_dimensions Tensor 1 chiều thuộc kiểu số nguyên (C7)
(I3) broadcast_dimensions Tensor hằng số 1 chiều thuộc kiểu số nguyên (C2-C6)
(I4) known_expanding_dimensions Tensor hằng số 1 chiều thuộc loại số nguyên (C8-C9)
(I5) known_nonexpanding_dimensions Tensor 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 lượng tử hoá (C1), (C3), (C5-C7)

Giới hạn

  • (C1) element_type(result) được xác định bằng:
    • 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ả d trong axes(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

Toán tử này giống hệt về chức năng với toán tử convolution (tích chập), nhưng khoảng đệm được chỉ định động 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 lượng tử hoá theo tensor (C1), (C10-C11), (C14) (C25), (C26-C27), (C30-C31), (C33)
(I2) rhs tensor hoặc tensor lượng tử hoá (C1), (C14-C16), (C26-C28), (C30-C33)
(I3) padding Tensor 2 chiều thuộc kiểu 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ố 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ố loại si64 (C20)
(I15) output_feature_dimension hằng số 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 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) Cho 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 trước 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 toán tử sử dụng tensor không được lượng tử hoá:
    • (C27) element_type(lhs) = element_type(rhs) = element_type(result).
  • Nếu thao tác sử dụng tensor lượng tử hoá:
    • (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 là 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 giá trị là !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

Toán tử này có chức năng giống với toán tử gather, với slice_sizes được chỉ định một cách linh động dưới dạng 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á theo 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ố 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á theo 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 của 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 sẽ 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]]
//            ]
//          ]

Ví dụ khác

dynamic_iota

Ngữ nghĩa

Toán tử này giống hệt về chức năng với toán tử iota, nhưng hình dạng kết quả được chỉ định độ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 số nguyên, dấu phẩy động hoặc loại 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

Toán tử này có chức năng giống với toán tử đệm, nhưng với edge_padding_low, edge_padding_highinterior_padding được chỉ định linh động dưới dạng 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á theo 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 kiểu 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 lượng tử hoá theo 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 với thao tác đổi hình dạng, nhưng hình dạng kết quả được chỉ định một cách linh hoạt 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 lượng tử hoá (C1-C3)
(I2) output_shape Tensor 1 chiều thuộc kiểu số nguyên (C4)

Kết quả đầu ra

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

Giới hạn

  • (C1) element_type(result) được xác định bằng:
    • 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, nếu không.
  • (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 khởi đầu được tính toán tự động và tạo ra một tensor result. start_indices chứa các chỉ mục bắt đầu của lát cắt cho mỗi phương diện có thể được điều chỉnh và slice_sizes chứa kích thước của lát cắt cho mỗi phương diện. 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á theo tensor (C1), (C2), (C4)
(I2) start_indices số biến thiên của tensor 0 chiều thuộc kiểu 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á 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 cắt bắt đầu từ start_indices được cập nhật bằng các giá trị trong update. Chính thức 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.
  • 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á theo tensor (C1-C4), (C6)
(I2) update tensor hoặc tensor lượng tử hoá theo tensor (C2), (C3), (C6)
(I3) start_indices số lượng biến 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á 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 mũ 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ố thực độ chính xác đơn: exp từ IEEE-754.
  • Đối với số phức: số 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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]]

Ví dụ khác

exponential_minus_one

Ngữ nghĩa

Thực hiện phép toán mũ trừ một theo từng 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ố thực: expm1 từ IEEE-754.
  • Đối với số phức: số mũ phức trừ đi một.
  • Đố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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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 đầ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 nghịch đảo từ phức tạp đến phức tạp.
  • RFFT: Chuyển tiếp FFT thực sang phức.
  • IRFFT: FFT nghịch đảo từ số thực đến số phức (tức là lấy số phức, trả về số thực).

Chính thức hơn, vì hàm fft lấy tensor 1 chiều của các kiểu phức làm đầu vào, tạo ra các tensor 1 chiều có cùng loại với đầu ra và tính toán phép biến đổi Fourier riêng biệt:

Đố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, với hàm ifft có cùng chữ ký kiểu và tính toán hàm nghịch đảo của fft:

Đối với fft_type = IFFT, result được xác định là giá trị nghịch đảo của 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, ..., :]).

Hơn nữa, với hàm rfft dùng tensor 1 chiều của các kiểu dấu phẩy động, tạo ra tensor 1 chiều thuộc các kiểu phức tạp của cùng một ngữ nghĩa dấu phẩy động và hoạt động như sau:

  • rfft(real_operand) = truncated_result trong đó
  • complex_operand... = (real_operand..., 0.0).
  • complex_result = fft(complex_operand).
  • truncated_result = complex_result[:(rank(complex_result) / 2 + 1)].

(Khi phép biến đổi Fourier rời rạc được tính toán cho các toán hạng thực, các phần tử N/2 + 1 đầ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 bị cắt bớt để tránh tính toán các phần tử 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, với hàm irfft có cùng chữ ký kiểu và tính toán hàm nghịch đảo của rfft:

Đối với fft_type = IRFFT, result được xác định là giá trị nghịch đảo của 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 của dấu phẩy động hoặc loại phức (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 của dấu phẩy động hoặc loại phức tạp (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 khác nhau:
    • Nếu fft_type = FFT, element_type(operand)element_type(result) có cùng một loại 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 loại dấu phẩy động và element_type(result) là một loại phức tạp của cùng một ngữ nghĩa dấu phẩy động.
    • Nếu fft_type = IRFFT, element_type(operand) là một loại phức tạp và element_type(result) là một loại 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ấy phần nguyên theo phần tử của tensor operand và tạo ra một tensor result. Triển khai phép toán roundToIntegralTowardNegative theo thông số kỹ thuật IEEE-754. Đối với các kiểu lượng tử hoá, 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 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 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

thu thập

Ngữ nghĩa

Thu thập các lát cắt từ tensor operand từ các độ dời được chỉ định trong start_indices và tạo 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 số chỉ mục result mẫu và giải thích chi tiết về chỉ mục operand tương ứng.

thu thập

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).
    • [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].
    • full_start_index[d_operand] = 0 nếu không.
  • Đố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].
    • 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 vào 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 thì hành vi sẽ không xác định. Chính thức hơn, cho 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á 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á theo 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 của 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 kích thước của dimension đã cho của operand. Chính thức 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ỳ.

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á (C1)
(I2) dimension hằng số 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ộ dữ liệu operand và tạo ra một result. Chính thức 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 bộ dữ liệu (C1), (C2)
(I2) index hằng số loại si32 (C1), (C2)

Kết quả đầu ra

Tên Loại Giới hạn
result mọi loại được hỗ trợ (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))
  index = 0 : i32
} : (tuple<tensor<2xf32>, tuple<tensor<i32>>>) -> tensor<2xf32>
// %result: [1.0, 2.0]

 Các ví dụ khác

nếu

Ngữ nghĩa

Tạo kết quả từ việc thực thi đúng một hàm từ true_branch hoặc false_branch tuỳ thuộc vào giá trị của pred. Chính thức 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. Chính thức 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 của dấu phẩy động hoặc loại phức (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 định nghĩa là:
    • complex_element_type(element_type(operand)) nếu is_complex(operand).
    • element_type(operand) nếu không.

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]

Ví dụ khác

nguồn cấp dữ liệu

Ngữ nghĩa

Đọc dữ liệu từ nguồn cấp dữ liệu nội tuyến và tạo results.

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

results bao gồm các giá trị tải trọng xuất hiện trước và một 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 2 đầu ra riêng biệt để cải thiện tính 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ố loại string

Kết quả đầu ra

Tên Loại Giới hạn
results số biến thiên của tensor, tensor lượng tử hoá hoặc mã thông báo (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 các giá trị theo thứ tự tăng dần vào tensor output, bắt đầu từ 0 theo chiều iota_dimension. Chính thức 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 của số nguyên, dấu phẩy động hoặc kiểu phức hoặc tensor lượng tử hoá 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 toàn bộ 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 tensor y. Triển khai phép toán isFinite từ thông số kỹ thuật IEEE-754. Đối với các loại được lượng tử 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 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 của 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 logarit theo từng 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ố thực: log từ IEEE-754.
  • Đối với số phức: logarit 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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 lôgarit theo phần tử cộng với một phép toán 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ố thực: logp1 từ IEEE-754.
  • Đối với số phức: lôgarit phức cộng 1.
  • Đối với các kiểu đượ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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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]

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 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: division(1, addition(1, exp(-x))) từ IEEE-754.
  • Đối với số phức: 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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.

Chính thức hơn là 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 lượng tử hoá theo 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 tối đa trên các 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 boolean: logic OR.
  • Đối với số nguyên: số nguyên lớn nhất.
  • Đối với số thực: maximum từ IEEE-754.
  • Đối với số phức: giá trị tối đa theo thứ tự bảng chữ cái cho cặp (real, imaginary). Việc áp đặt thứ tự trên số phức 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á tính năng hỗ trợ số phức cho toán tử 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 lượng tử hoá theo tensor (C1)
(I2) rhs tensor hoặc tensor lượng tử hoá theo tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá 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]]

Ví dụ khác

tối thiểu

Ngữ nghĩa

Thực hiện phép toán tối thiểu trên các phần tử trên các tensor lhsrhs 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 boolean: AND logic.
  • Đối với số nguyên: số nguyên nhỏ nhất.
  • Đối với số thực: minimum từ IEEE-754.
  • Đối với số phức: giá trị tối thiểu trong từ điển cho cặp (real, imaginary). Việc áp đặt thứ tự trên số phức 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á tính năng hỗ trợ số phức cho toán tử 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 lượng tử hoá theo tensor (C1)
(I2) rhs tensor hoặc tensor lượng tử hoá theo tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá 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]]

Ví dụ khác

nhân

Ngữ nghĩa

Thực hiện tích 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 boolean: logic AND.
  • Đối với số nguyên: 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 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 lượng tử hoá theo tensor (C1)
(I2) rhs tensor hoặc tensor lượng tử hoá theo tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá 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 các phần tử của 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ố nguyên đã ký: 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 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 số nguyên, dấu phẩy động hoặc loại 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 của số nguyên, dấu phẩy động hoặc kiểu phức hoặc tensor lượng tử hoá 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 NOT theo phần tử của 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 boolean: NOT logic.
  • Đối với số nguyên: NOT bit.

Đố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 phép biế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, toán tử này là một giá trị nhận dạng, tức là result = operand.

Đối số

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

Kết quả đầu ra

Tên Loại Giới hạn
result số lượng tensor biến đổi, tensor lượng tử hoá trên mỗi tensor hoặc mã thông báo (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 thao tác 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 boolean: logic OR.
  • Đối với số nguyên: toán tử OR bit.

Thông tin đầu vào

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

Kết quả đầu ra

Tên Loại Giới hạn
result tensor của 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

nguồn cấp dữ liệu đầu ra

Ngữ nghĩa

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

Ngữ nghĩa của outfeed_config được xác định bằng 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 biến đổi hoặc tensor lượng tử hoá
(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

Ví dụ khác

đệ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 chỉ định lượng khoảng đệm được thêm vào ở đầu thấp (bên cạnh chỉ mục 0) và đầu cao (bên cạnh chỉ mục cao nhất) của mỗi kích thước tương ứ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 khoảng đệm được thêm vào giữa hai phần tử bất kỳ trong mỗi nhóm phiên bản và không được âm. Khoảng đệm bên trong xảy ra trước khoảng đệm cạnh, chẳng hạn 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.

Chính thức hơn, result[result_index] được định nghĩa là:

  • operand[operand_index] nếu result_index = edge_padding_low + operand_index * (interior_padding + 1).
  • padding_value 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á theo 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 lượng tử hoá theo 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 tính số lượng bit theo phần tử trong tensor operand và tạo một tensor 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 mũ theo phần tử của tensor lhs theo 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: số mũ nguyên.
  • Đối với số thực: pow từ IEEE-754.
  • Đối với số phức: số 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 số nguyên, dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)
(I2) rhs tensor số nguyên, dấu phẩy động hoặc loại 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 của số nguyên, dấu phẩy động hoặc kiểu phức hoặc tensor lượng tử hoá 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]

Ví dụ khác

thực

Ngữ nghĩa

Trích xuất phần thực, theo phần tử, từ operand và tạo một tensor result. Trang trọng hơn, cho 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 của dấu phẩy động hoặc loại phức (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 định nghĩa là:
    • complex_element_type(element_type(operand)) nếu is_complex(operand).
    • element_type(operand) nếu không.

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 results.

Nếu is_host_transfertrue, thì thao tác sẽ chuyển dữ liệu từ máy chủ. Nếu không, ứng dụng sẽ chuyển dữ liệu từ một thiết bị khác. Điều này có nghĩa là được xác định bằng cách triển khai. 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 số đó (#666).

results bao gồm các giá trị tải trọng xuất hiện trước và một mã thông báo xuất hiện sau cùng. Trong tương lai, chúng tôi dự định tách 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 (C4)
(I2) channel_id hằng số thuộc loại si64
(I3) channel_type enum của DEVICE_TO_DEVICEHOST_TO_DEVICE (C1)
(I4) is_host_transfer hằng số thuộc loại i1 (C1)

Kết quả đầu ra

Tên Loại Giới hạn
results số biến thiên của tensor, tensor lượng tử hoá hoặc mã thông báo (C2-C4)

Giới hạn

  • (C1) channel_type được xác định là:
    • HOST_TO_DEVICE nếu is_host_transfer = true,
    • DEVICE_TO_DEVICE nếu không.
  • (C2) 0 < size(results).
  • (C3) is_empty(result[:-1]) hoặc is_tensor(type(results[:-1])).
  • (C4) is_token(type(results[-1])).

Ví dụ

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

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 là do phương thức triển khai xác định, nghĩa là bodyinit_values phải tạo thành một đơn sắc để đảm bảo rằng thao tác đó tạo ra kết quả như nhau cho tất cả dữ liệu đầu vào trên tất cả các phương thức triển khai. Tuy nhiên, điều kiện này không ảnh hưởng đến nhiều mức giảm phổ biến. Ví dụ: phép cộng dấu phẩy động cho body và 0 cho init_values không thực sự tạo thành một monoid vì phép cộng dấu phẩy động không mang tính liên kết.

Chính thức hơn là 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 đủ được xác định bằng cách triển khai, trong đó truyền tải 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ố biến thiên của tensor 0 chiều hoặc tensor lượng tử hoá 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ó 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).
  • (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 theo phần tử của operand thành một loại dấu phẩy động khác sử dụng exponent_bitsmantissa_bits, sau đó quay lại loại dấu phẩy động ban đầu và tạo một tensor output.

Chính thức hơn:

  • Các bit dấu phẩy động 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ố bit dấu phẩy động của giá trị ban đầu, thì các bit dấu phẩy động 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 phù hợp với phạm vi do exponent_bits cung cấp, thì kết quả trung gian sẽ tràn đến vô cùng bằng cách sử dụng dấu ban đầu hoặc sẽ tràn về 0 bằ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 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 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]

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 việc rút gọn 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ả rút gọn dọc theo scatter_dimension thành các phần và phân tán các phần được 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 định nghĩa 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á theo tensor (C1), (C2), (C7), (C8)
(I2) scatter_dimension hằng số 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ố 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á 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 định nghĩa 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 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 rút gọn body cho các cửa sổ của inputsinit_values và 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

Chính thức hơn, results...[result_index] = reduce(windows, init_values, axes(inputs...), body) (xem giảm) 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ố biến thiên của tensor 0 chiều hoặc tensor lượng tử hoá 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 trong [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]]

Ví dụ khác

phần dư

Ngữ nghĩa

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

Chính thức 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. Phần còn lại được tính là lhs - d * rhs, trong đó d được tính theo:

  • Đố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: TBD (#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 loại 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 của IEEE-754, trong đó d là một giá trị tích phân gần với giá trị chính xác của lhs/rhs có các mối liên kết với nhau.

Thông tin đầu vào

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

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

đổi hình dạng

Ngữ nghĩa

Thực hiện đị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ách trình bày chính 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>.

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 lượng tử hoá (C1-C3)

Kết quả đầu ra

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

Giới hạn

  • (C1) element_type(result) được xác định bằng:
    • 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, nếu không.
  • (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ác phần tử trong operand dọc theo dimensions đã chỉ định và tạo một tensor result. 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.
  • operand_index[d] = result_index[d] 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á theo 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á theo 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 các số ngẫu nhiên bằng thuật toán rng_distribution và tạo ra tensor result có hình dạng cho trước shape.

Nếu là rng_distribution = UNIFORM, thì các số ngẫu nhiên sẽ được tạo theo hàm phân phối đồng nhất trong khoảng thời gian [a, b). Nếu là a >= b, thì 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 trung bình = a và độ lệch chuẩn = b. Nếu là b < 0, hành vi sẽ không xác định.

Việc triển khai xác định cách chính xác cách tạo số ngẫu nhiên. Ví dụ: các biến này có thể xác định trước hoặc không xác định trước, đồng thời 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, cơ chế này đã được xem là không dùng nữa. Vì vậy, trong tương lai, chúng tôi dự định tìm hiểu việc loại bỏ cơ chế 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 của kiểu 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 của số nguyên, boolean hoặc loại 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 các bit ngẫu nhiên đồng nhất và trạng thái đầu ra đã 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 với trạng thái ban đầu initial_state. Kết quả đượ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 lần 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ể được xác định bằng cách triển khai của thuật toán Threefry.*
  • PHILOX: Biến thể do phương thức triển khai xác định của thuật toán Philox.*

* Xem: Salmon et al. SC 2011. Số ngẫu nhiên song song: dễ dàng như việc đế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 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 của 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 định nghĩa là:
    • do phương thức 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 làm tròn theo phần tử về số nguyên gần nhất, tách các giá trị bằng 0 khỏi nhau trên tensor operand và tạo ra tensor result. Triển khai phép toán roundToIntegralTiesToAway theo thông số kỹ thuật IEEE-754. Đối với các loại được lượng tử hoá, 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 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 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]

Ví dụ khác

round_nearest_even

Ngữ nghĩa

Thực hiện làm tròn theo phần tử về số nguyên gần nhất, phá vỡ mối quan hệ ràng buộc về số nguyên chẵn, trên tensor operand và tạo ra tensor result. Triển khai toán tử roundToIntegralTiesToEven từ quy cách của IEEE-754. Đối với các loại được lượng tử hoá, 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 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 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 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: 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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

tán xạ

Ngữ nghĩa

Tạo các tensor results bằng các tensor inputs, ngoại trừ việc 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ơ đồ dưới đây cho thấy cách các phần tử trong updates... ánh xạ trên các phần tử trong results... bằng một ví dụ cụ thể. Sơ đồ này chọn một số chỉ mục updates... mẫu và giải thích chi tiết về chỉ mục results... tương ứng.

tán xạ

Chính thức hơn, đối với tất cả 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 định nghĩa 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).
    • [scatter_indices[update_scatter_index]] nếu không.
  • Đố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].
    • full_start_index[d_input] = 0 nếu không.
  • Đố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].
    • full_batching_index[d_input] = 0 nếu không.
  • 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 vào các chỉ mục từ inserted_window_dimsinput_batching_dims.
  • result_index = full_start_index + full_batching_index + full_window_index.

Do đó, 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) trong đó:
    • Nếu result_index nằm trong giới hạn 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 đượ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ố loại si64 (C4), (C16), (C19), (C22)
(I10) indices_are_sorted hằng số loại i1
(I11) unique_indices hằng số 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)
    • kích thước(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 sẽ không được đưa vào.
    • update_window_dim_sizes <= shape(inputs[0]) ngoại trừ việc không bao gồm các kích thước kích thước trong inputs[0] tương ứng với inserted_window_dimsinput_batching_dims.
    • 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]]
//           ]
//          ]
%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 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. Chính thức 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 lượng tử hoá, 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 lượng tử hoá theo tensor (C1-C2)
(I3) on_false tensor hoặc tensor lượng tử hoá theo tensor (C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor hoặc tensor lượng tử hoá theo 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ừ tensor source bằng scatter dựa trên kết quả của reduce_window của tensor input bằng select và tạo ra một tensor result.

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

select_and_scatter

Chính thức hơn:

  • selected_values = reduce_window_without_init(...) với các giá trị nhập 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ị khởi tạo. 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á theo tensor (C1-C4), (C6), (C8-C11)
(I2) source tensor hoặc tensor lượng tử hoá theo 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 lượng tử hoá theo 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ó kiểu (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 và tạo mã thông báo result.

Nếu is_host_transfertrue, thì thao tác này sẽ chuyển dữ liệu đến máy chủ. Nếu không, thiết bị sẽ chuyển dữ liệu sang một thiết bị khác. Điều này có nghĩa là được xác định bằng cách triển khai. Cờ này sao ché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 các cờ này (#666).

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á
(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 (C1)
(I5) is_host_transfer hằng số thuộc loại i1 (C1)

Kết quả đầu ra

Tên Loại
result token

Giới hạn

  • (C1) channel_type được xác định là:
    • DEVICE_TO_HOST nếu is_host_transfer = true,
    • DEVICE_TO_DEVICE nếu không.

Ví dụ

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

 Các ví dụ khác

shift_left

Ngữ nghĩa

Thực hiện phép toán dịch sang trái theo phần tử trên tensor lhs theo số bit rhs và tạo ra một 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 thao tác dịch chuyển sang phải số học thông minh trên tensor lhs theo số 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, 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 phép dịch chuyển sang phải theo logic theo phần tử trên tensor lhs theo số bit rhs và tạo ra một 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, 8]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_right_logical"(%lhs, %rhs): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %result: [9223372036854775807, 0, 1]

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. Chính thức 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 lượng tử hoá, 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 kiểu phức hoặc tensor lượng tử hoá 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 kiểu phức hoặc tensor lượng tử hoá 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 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ố 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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 khởi đầu được tính toán cố định và tạo ra một tensor result. start_indices chứa các chỉ mục bắt đầu của lát cắt cho mỗi phương diện, limit_indices chứa các chỉ mục kết thúc (riêng) của lát cắt cho mỗi phương diện và strides chứa các bước di chuyển cho mỗi phương diện.

Chính thức 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á theo 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á 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 lát cắt 1 chiều của inputs dọc theo chiều dimension với nhau, theo comparator và tạo ra results.

Không giống như các giá trị đầu vào tương tự trong các phép toán 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, việc này có thể không được cho phép vì lý do nhất quán (#1377).

Nếu is_stable đúng, 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 coi là bằng nhau theo trình so sánh sẽ được giữ nguyên. Đối với trường hợp chỉ có một đầu vào, hai phần tử e1e2 được trình 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á cho nhiều đầu vào.

Chính thức hơn, đối với tất cả 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ần, dự kiến rằng comparator_together sẽ trả về true nếu đối số bên trái nhỏ hơn đối số thứ hai 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ố biến thiên của tensor hoặc tensor lượng tử hoá mỗi tensor (C1–C5)
(I2) dimension hằng số 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 tính căn bậc hai 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 thực hiện các thao tác sau:

  • Đối với số thực độ chính xác đơn: squareRoot từ IEEE-754.
  • Đối với số phức: căn bậc hai 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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 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: 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 số nguyên, dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor (C1)
(I2) rhs tensor số nguyên, dấu phẩy động hoặc loại 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 của số nguyên, dấu phẩy động hoặc kiểu phức hoặc tensor lượng tử hoá 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 của các 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: tang 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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]
//          ]

Ví dụ khác

tanh

Ngữ nghĩa

Thực hiện phép toán hàm tang hyperbol 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ố thực: tanh từ IEEE-754.
  • Đối với số phức: tan hyperbol 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor dấu phẩy động hoặc loại 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

hoán vị

Ngữ nghĩa

Hoán đổi các phương diện của tensor operand bằng permutation và tạo ra một tensor result. Chính thức hơn là 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 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 lượng tử hoá (C1), (C3-C4)

Giới hạn

  • (C1) element_type(result) được xác định bằng:
    • 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, nếu không.
  • (C2) permutation là một phép 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 dưới hoặc trên.

Chính thức hơn, với 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ởi transpose_a, có thể là một trong những giá trị sau:

  • NO_TRANSPOSE: Thực hiện thao tác bằng cách sử dụng a như hiện có.
  • TRANSPOSE: Thực hiện phép toán trên phép chuyển đổi của a.
  • ADJOINT: Thực hiện thao tác hoá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 được xác định bằng cách triển khai.

Nếu unit_diagonal là đúng, 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 thì hành vi sẽ không xác định.

Đối với các loại được lượng tử hoá, 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 của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor (C1-C3)
(I2) b tensor dấu phẩy động hoặc loại 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ố 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 dấu phẩy động hoặc loại 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]
//          ]

bộ dữ liệu

Ngữ nghĩa

Tạo một bộ dữ liệu 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ị biến (C1)

Kết quả đầu ra

Tên Loại Giới hạn
result bộ dữ liệu (C1)

Giới hạn

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

Ví dụ

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

 Các ví dụ khác

uniform_dequantize

Ngữ nghĩa

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

Chính thức 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 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 chuyển đổi theo 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.

Chính thức hơn,

  • Nếu is_float(operand):
    • result = quantize(operand, type(result)).
  • Nếu giá trị là 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 dấu phẩy động hoặc loại lượng tử hoá (C1), (C2)

Kết quả đầu ra

Tên Loại Giới hạn
result tensor 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]

while

Ngữ nghĩa

Tạo ra kết quả từ việc thực thi hàm body 0 lần trở lên trong khi hàm cond xuất ra true. Chính thức hơn, bạn có thể biểu thị ngữ nghĩa bằng cách sử dụ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 là TBD (#383).

Thông tin đầu vào

Hãng nhạc Tên Loại Giới hạn
(I1) operand số lượng tensor, tensor lượng tử hoá hoặc mã thông báo có thể 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 tensor, tensor lượng tử hoá hoặc mã thông báo có thể thay đổi (C3)

Giới hạn

  • (C1) cond có loại (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 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 thực hiện các thao tác sau:

  • Đối với boolean: XOR logic.
  • Đối với số nguyên: XOR bit.

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 tự nhiên đôi khi chứa các thao tác mà StableHLO chưa xác định.

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

StableHLO sử dụng các thao tác MLIR ngược dòng cho ModuleOp, FuncOp, CallOp và ReturnOp. Điều này được thực hiện để tăng khả năng tương tác tốt hơn với máy móc MLIR hiện có, vì nhiều đường 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ó những hoạt động này. Các hoạt động này được đảm bảo tương thích đầ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á), 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

Tập hợp các toán tử CHLO chứa các toán tử cấp cao hơn phân ly thành StableHLO. Hiện tại, chúng tôi không đảm bảo khả năng tương thích của CHLO. Để đảm bảo khả năng tương thích, bạn phải sử dụng thẻ thông hành cho phép hợp pháp hoá từ clo-legalize-to-stablehlo trước khi chuyển đổi tuần tự.

Phép toán về hình dạng

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

Dynamism RFC > O2 cho biết các thuộc tính này nằm ngoài phạm vi. Tuy nhiên, một số tính năng hỗ trợ cho các loại index được đưa vào cho mục đích tương tác. Không có gì đảm bảo về khả năng tương thích của các thao tác hoặc loại này. Bạn có thể sử dụng thẻ truyền shape-legalize-to-stablehlo để chuyển đổi các toán tử này thành toán tử 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 không còn được dùng nữa và sắp ngừng hoạt động trong StableHLO. Bạn có thể xem toàn bộ thông tin chi tiết về các lần xoá này trong StableHLO v1.0 Cleanup #2283. Vấn đề theo dõi đối với các tính năng ngừng hoạt động 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 thao tác StableHLO – ban đầu, các thao tác này là một phần của nhóm thao tác StableHLO nhưng sau đó được coi là không phù hợp: broadcast, create_token, cross-replica-sum, dot, einsum, torch_index_select, unary_einsum (#3).
  • Thao tác không dùng đến – Các 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 quy trình sử dụng các thao tác này đã được tái cấu trúc để không cần đến các thao tác đó 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).

Bạn có thể dễ dàng xoá một số thao tác này 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 khoảng thời gian tương thích hiện tại kết thúc (6 tháng). Các thao tác khác vẫn đang được xem xét để xoá (einsum, get_tuple_element, map, rng torch_index_select, tuple, complex so sánh, window_reversal). Trong khi chờ ý kiến phản hồi của cộng đồng, các thao tác này sẽ bị xoá hoặc được thêm vào thông số kỹ thuật với khả năng hỗ trợ đầy đủ. Cho đến khi các hợp đồng tương lai về hoạt động này được biết, 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ự

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. Giá trị đầu ra của một hàm được tính toán bằng cách thực thi biểu đồ của các toán tử bắt nguồn từ toán tử return tương ứng.

Thứ tự thực thi được xác định bằng cách triển khai, miễn là thứ tự đó phù hợp với dòng dữ liệu, tức là nếu các toán tử được thực thi trước khi sử dụng. Trong StableHLO, tất cả các thao tác có hiệu ứ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 đaплекс thành một mã thông báo thông qua after_all), vì vậy, thứ tự thực thi của các hiệu ứng phụ cũng được điều chỉnh cho phù hợp với luồng dữ liệu. Ví dụ: trong chương trình bên dưới có thể có hai thứ tự thực thi: %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>
}

Nói một cách chính thức hơn, quy trình StableHLO là sự kết hợp của: 1) 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 bắt đầu bằng các giá trị đầu vào cho hàm main, tiến triển qua biểu đồ của các thao tác cập nhật trạng thái thao tác và 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 lưới quy trình 2D của num_replicas theo num_partitions, cả hai đều có loại ui32.

Trong lưới quy trình StableHLO, num_replicas * num_partitions của các 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 lưới quy trình này vào 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, tất cả các chương trình đều có thể giống nhau (theo kiểu "Một chương trình, nhiều dữ liệu"), đều có thể khác nhau (theo kiểu "Nhiều chương trình, Nhiều dữ liệu") hoặc một kiểu nào đó ở giữa. Trong tương lai, chúng tôi dự định sẽ hỗ trợ các thành ngữ 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 như độc lập với nhau – các quy trình này có trạng thái hoạt động riêng biệt, giá trị đầu vào/giữa/đầu ra riêng biệt và hầu hết các thao tác được thực thi riêng biệt 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.

Do 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 việc tham chiếu đến các giá trị này theo tên của chúng thường không gây nhầm lẫn. Tuy nhiên, khi mô tả ngữ nghĩa của các toán tử tập hợp, điều đó là chưa đủ và dẫn đến ký hiệu name@process_id để tham chiếu đến giá trị name trong một quy trình cụ thể. (Dưới góc độ đó, name không đủ điều kiện có thể được xem là 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 bằng cách triển khai, ngoại trừ việc đồng bộ hoá do giao tiếp điểm-điểm và các thao tác tập thể đưa ra như mô tả dưới đây.

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. Một 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 hoạt động, 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, ví dụ: nguồn gốc của các mã nhận dạng kênh này, cách các chương trình xử lý nhận biết được các mã nhận dạng kênh này và loại đồng bộ hoá mà các mã nhận dạng kênh này đưa ra, sẽ được xác định sau (#484).

Truyền thông liên lạc

Mỗi quy trình StableHLO đều có quyền truy cập vào hai giao diện truyền trực tuyến:

  • Infeed có thể đọc được.
  • Luồng truyền dẫn có thể ghi vào.

Không giống như các kênh dùng để giao tiếp giữa các quy trình và do đó có các quy trình ở cả hai đầu, nguồn cấp dữ liệu đầu vào và đầu ra có quy trình triển khai đầu kia được xác định.

Việc chính thức hoá thêm, ví dụ: 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á được giới thiệu, là TBD (#484).

Toán tử tập hợp

Có 6 toán tử tập hợp trong StableHLO: all_gather, all_reduce, all_to_all, collective_broadcast, collective_permutereduce_scatter. Tất cả các toán tử này 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ể tạo ra một rào cản đồng bộ hoá. Việc chính thức hoá thêm, ví dụ: giải thích chi tiết về 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 các quy trình đến rào cản này và những gì sẽ xảy ra nếu không có quy trình nào đến rào cản này, là TBD (#484).

Nếu nhóm quy trình liên quan đến hoạt động 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 toán tử tập thể cần có một kênh và toán tử 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 kênh.

Các phép tính do toán tử tập thể thực hiện là dành riêng cho từng toán tử và được mô tả trong các phần toán tử 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 được dùng chung giữa các hoạt động này và được mô tả trong phần này. Nói một cách chính thức hơn, StableHLO hỗ trợ bốn 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 – một danh sách các danh sách mã bản sao – và tính toán tích Descartes 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. Chính thức hơn, hãy 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 xảy ra trong mỗi nhóm quy trình. Chiến lược này lấy partition_groups – một danh sách các danh sách mã phân vùng – và tính toán tích Descartes của partition_groups theo replica_ids. partition_groups phải có các phần tử riêng biệt và bao gồm tất cả partition_ids. Chính thức hơn, hãy 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à hoạt động giao tiếp giữa các phân vùng đều có thể xảy ra trong mỗi nhóm quy trình. Chiến lược này lấy replica_groups – một danh sách các danh sách mã bản sao – và tính toán các sản phẩm Cartesian 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. Chính thức hơn, hãy 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 "đã làm phẳng" ở dạng replica_id * num_partitions + partition_id – và chuyển đổi các danh sách đó 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. Chính thức 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 [[(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 về độ chính xác về số, 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 lượng tử

Cách diễn giải các phép toán StableHLO được lượng tử hoá có thể khác nhau tuỳ thuộc vào yêu cầu và chức năng phần cứng. Ví dụ: một số phần cứng có thể chọn diễn giải các phép toán lượng tử hoá bằng chiến lược "giải lượng tử hoá, thực hiện phép toán dấu phẩy động và cuối cùng là lượng tử hoá". Các thuật toán 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 thao tác StableHLO được lượng tử hoá chỉ được xác định bằng cách triển khai cụ thể. Việc diễn giải quá trình lượng tử hoá kết hợp (#1575) phải dựa trên ngữ nghĩa của quá trình này như quy định trong quy cách (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 các quy tắc ràng buộc rộng rãi cho từng toán tử, loại trừ 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, ví dụ: thông qua tràn số nguyên, truy cập ngoài giới hạn, v.v. Trừ phi được gọi rõ ràng, tất cả các lỗi này đều dẫn đến hành vi do phương thức 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

Ngoại lệ đối với quy tắc này là 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 ngoại lệ được xác định theo tiêu chuẩn IEEE-754 (thao tác không hợp lệ, chia cho 0, tràn, tràn hoặc ngoại lệ không chính xác) sẽ tạo ra kết quả mặc định (như xác định trong tiêu chuẩn) và tiếp tục thực thi mà không cần nâng cờ trạng thái tương ứng; tương tự như cách xử lý ngoại lệ raiseNoFlag từ tiêu chuẩn. Các trường hợp ngoại lệ cho các thao tác không 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 bằng 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 thống nhất trong thời gian chạy, nếu không thì hành vi sẽ không xác định. StableHLO không cung cấp rõ ràng một toán tử có thể xác nhận rằng một tensor có hình dạng nhất định trong 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, 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 sử dụng phiên bản ISO đã sửa đổi của cú pháp EBNF (ISO/IEC 14977:1996, Wikipedia), với hai sửa đổi: 1) các quy tắc được xác định bằng ::= thay vì =,

2) việc nối được biểu thị bằng cách đặt vị trí cạnh nhau thay vì ,.

Để mô tả ngữ nghĩa (tức là trong các phần "Loại", "Hằng số" và "Thao tác"), chúng ta đang sử dụng các công thức dựa trên cú pháp Python được mở rộng với tính năng hỗ trợ để thể hiện ngắn gọn các thao tác trên mảng như mô tả bên dưới. Cách này hoạt động tốt 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 ta sử dụng cú pháp Python cơ bản luôn được giới thiệu rõ ràng.

Công thức

Hãy cùng tìm hiểu cách thức 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 quy tắc ràng buộc cho toán tử này có dạng như sau: dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...).

Tên dùng trong công thức này đến từ 2 nguồn: 1) hàm toàn cục, 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à đầu vào lhs, lhs_batching_dimensions, rhsrhs_batching_dimensions đượ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 tính ngắn gọn. Để hiểu rõ công thức, hãy biến đổi công thức đó thành cú pháp vanilla Python.

A) Trong các công thức này, chúng ta đang sử dụng = để biểu thị sự bằng nhau, 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 (...) biến các biểu thức vô hướng thành biểu thức tensor. Tóm lại, f(xs...) có nghĩa là "đối với mỗi x vô hướng trong tensor xs, hãy tính toán một f(x) vô hướng, sau đó 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 cơ bản, công thức mẫu 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 được hoạt động ở cấp độ vô hướng riêng lẻ. Tuy nhiên, trong một số trường hợp phức tạp, có thể sử dụng cú pháp bán không chính thức cấp thấp hơn như trong công thức start_indices[bi0, ..., :, ..., biN] từ thông số kỹ thuật gather. Để đảm bảo tính ngắn gọn, chúng tôi không cung cấp một cách thức chính xác để dịch cú pháp như vậy sang Python cơ bản, hy vọng rằng bạn vẫn có thể hiểu được cú pháp này theo từng trường hợp. Vui lòng cho chúng tôi biết nếu một số công thức cụ thể có vẻ không rõ ràng. 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 tất cả các loại danh sách, bao gồm cả tensor, danh sách tensor (ví dụ: có thể phát sinh từ một số lượng tensor biến đổ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 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) mà 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 ẩn. Mặc dù tập hợp các toán tử StableHLO không hỗ trợ tính năng truyền tin ngầm ẩn, nhưng các công thức lại hỗ trợ tính năng này, cũng như giúp rút gọn. Tóm lại, nếu một đại lượng vô hướng được sử dụng trong ngữ cảnh dự kiến sẽ có một tensor, 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, hãy thiết lập một quy tắc ràng buộc khác: 0 <= lhs_batching_dimensions < rank(lhs). Như được xác định trong thông số kỹ thuật dot_general, lhs_batching_dimensions là một tensor, tuy nhiên cả 0rank(lhs) đều là vô hướng. Sau khi chúng ta áp dụng tính năng truyền phát 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 toán tử dot_general cụ thể, công thức này sẽ đánh giá thành một tensor của 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ẽ được duy trì nếu công thức có giá trị true hoặc cho một tensor chỉ có phần tử true.

Tên

Trong công thức, phạm vi từ vựng bao gồm: 1) hàm toàn cục, 2) định nghĩa thành viên,

3) định nghĩa cục bộ. Danh sách các hàm toàn cục được cung cấp bên dưới. Danh sách định nghĩa 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 toán tử, định nghĩa thành phần bao gồm các tên được giới thiệu trong phần "Đầu vào" và "Đầu ra".
  • Đối với mọi phần tử 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 phải ký tự đầu cuối EBNF tương ứng. Hầu hết các 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 phải là đầu cuối sang kiểu snake case (ví dụ: IntegerLiteral => integer_literal), nhưng đôi khi tên được viết tắt trong quá trình này (ví dụ: QuantizationStorageType => storage_type). Trong trường hợp đó, tên được giới thiệu rõ ràng tương tự như các phần "Đầu vào"/"Đầu ra" trong thông số kỹ thuật hoạt động.
  • Ngoài ra, các định nghĩa về thành phần luôn bao gồm self để tham chiếu đến thành phần của 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 các loại giá trị sau: 1) Value (giá trị thực tế, ví dụ: dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>; chúng luôn biết loại của mình), 2) Placeholder (giá trị trong tương lai, ví dụ: lhs, rhs hoặc result; giá trị thực tế của chúng chưa được biết, chỉ biết loại của chúng), 3) Type (các loại được xác định trong phần "Loại"), 4) Function (các hàm toàn cục được xác định trong phần "Hàm").

Tuỳ thuộc vào ngữ cảnh, tên có thể tham chiếu đến các giá trị khác nhau. Cụ thể hơn, phần "Ngữ nghĩa" cho các hoạt động (và phần tương đương cho các phần tử khác của chương trình) xác định logic thời gian chạy, vì vậy, tất cả dữ liệu đầu vào đều có sẵn dưới dạng Value. Ngược lại, phần "Constraints" (Giới hạn) cho các toán tử (và các toán tử tương đương) xác định logic "thời gian biên dịch", tức là một nội dung thường được thực thi trước thời gian chạy, vì vậy, chỉ có các giá trị đầu vào không đổi dưới dạng Value và các giá trị đầu vào khác chỉ có sẵn dưới dạng Placeholder.

Tên Trong phần "Ngữ nghĩa" Trong phần "Giới hạn"
Hàm toàn cục Function Function
Đầu vào hằng số Value Value
Giá trị đầu vào không cố định Value Placeholder
Kết quả đầu ra Value Placeholder
Định nghĩa cục bộ 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ố nên có sẵn dưới dạng Value trong cả ngữ nghĩa và quy tắ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ỉ có sẵn dưới dạng Placeholder trong các quy tắ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 loại. Thay vào đó, chúng tôi trực tiếp sử dụng 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 kiểu

  • element_type được xác định trên các loại tensor và các loại tensor 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 có thể nâng cấp loại x lên loại 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 quảng bá cụ thể này hiện được sử dụng trong ngữ cảnh tính toán 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. Có sẵn cho tất cả các loại. Ví dụ: is_float(x) 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ị lớn nhất của một TensorElementType. Nếu x không phải là TensorElementType, hãy trả về None.

  • min_value(x: Type) -> Value trả về giá trị tối thiểu có thể của một TensorElementType. Nếu x không phải là TensorElementType, hãy trả về None.

  • member_name(x: Value | Placeholder | Type) -> Any. Có sẵn 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 sẽ là lối tắt cho member_name(type(x)). Nếu x không phải là loại có thành phần thích hợp, hoặc giá trị/phần giữ chỗ thuộc loại như vậy, hàm 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 được xác định trong quá trình triển khai, vì vậy, việc chỉ định giá trị mặc định sẽ không chính xác.

Xây dựng giá trị

  • operation_name(*xs: Value | Type) -> Value. Hỗ trợ mọi hoạt động. Ví dụ: add(lhs, rhs) nhận hai giá trị tensor lhsrhs rồi trả về kết quả đánh giá toán tử add với các giá trị đầu vào này. Đối với một số toán tử (ví dụ: broadcast_in_dim), loại dữ liệu đầu ra của các toán tử đó là "tải trọng", tức là cần thiết để đánh giá một toán tử. 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 giá trị

  • Tất cả các toán tử và hàm của Python đều có sẵn. Ví dụ: cả ký hiệu subscription (đăng ký) và slicing (cắt) từ Python đều có thể được dùng để lập chỉ mục vào tensor, tensor 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ó một 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 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à tensor, thì sẽ trả về None.

  • is_sorted(x: Value) -> Value được xác định trên 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 tương ứng với thứ tự từ điển tăng dần của các chỉ mục tương ứng hoặc nếu không thì false. Nếu x không phải là một tensor, hãy 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ó phần tử trùng lặp hoặc false nếu không. Nếu x không phải là tensor, thì sẽ trả về None.

  • member_name(x: Value) -> Any được xác định cho tất cả các định nghĩa thành phần member_name của tất cả các giá trị. Ví dụ: real_part(x) trả về phần RealPart của một ComplexConstant tương ứng. Nếu x không phải là một giá trị có thành phần thích hợp, hãy trả về None.

  • same(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 x đều bằng nhau hoặc false nếu không. Nếu tensor không có phần tử, thì tensor đó được tính là "tất cả đều bằng nhau", tức là hàm sẽ trả về true. Nếu x không phải là tensor, hãy 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 cắt num_results của x dọc theo trục axis. Nếu x không phải là tensor hoặc dim(x, axis) % num_results != 0, hãy 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 toán tử 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 toán tử hợp lệ, tức là 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ề các 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à loại tensor, loại tensor lượng tử hoá hoặc giá trị hoặc phần giữ chỗ của một trong các loại này, hãy 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 có trên các kiểu" qua member_name.

  • size(x: Value | Placeholder | Type) -> Value là lối tắt cho reduce(lambda x, y: x * y, shape(x)).

Tính toán 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 các loại này thành "đường cơ sở", tức là một loại có cùng hình dạng nhưng với các thông số lượng tử hoá của loại phần tử được đặt lại thành giá trị mặc định. Đây được coi là một thủ thuật tiện dụng để so sánh cả tensor lượng tử hoá và loại tensor lượng tử hoá một cách không chính xác. Việc này khá thường xuyên cần đến. Đối với các loại lượng tử hoá, điều này cho phép các loại so sánh 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 lượng tử hoá trên mỗi trục) phải khớp 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 lượng tử hoá và biến đổi các loại tensor đó 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á đại diện cho 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 không 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à biến đổi các loại tensor đó thành các loại tensor 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 bộ nhớ bằng cách sử dụng điểm không và tỷ lệ được 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á. Phương thức này giải lượng tử hoá, tức là chuyển các phần tử lượng tử hoá thành loại được biểu thị, sau đó thực hiện một phép toán, rồi lượng tử hoá, tức là chuyển kết quả trở lại loại bộ nhớ. Hiện tại, hàm này chỉ hoạt động cho việc lượng tử hoá theo từng tensor. Đang tiến hành định lượng trên mỗi trục (#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 dùng để chỉ định lượng tử hoá chỉ trọng số cho op kết hợp chấp nhận lh trong dấu phẩy động và rhs trong các kiểu lượng tử hoá. Hàm 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 phép tính bằng số thực. Loại phần tử của tensor lhs float và loại biểu thị của tensor rhs quantized phải giống hệt 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 mục "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

Giá trị StableHLO có thể có kích thước phương diện độ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 (động lực không được xếp hạng, ví dụ: tensor<*xi64>). 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ó quy tắc ràng buộc về kích thước. Các quy tắc ràng buộc sẽ được xác minh tĩnh nếu có thể, nếu không thì các quy tắc đó sẽ bị trì hoãn trong thời gian chạy và sự 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 đối với các toán tử đơn 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
}

Một chương trình như vậy là không bình thường, vì thông thường, bạn sẽ biết hình dạng của kết quả nhưng không biết hình dạng của dữ liệu đầ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 toán tử abs trong chương trình này vì không xác định được hình dạng chính xác của toán hạng. Tuy nhiên, các hình dạng này chắc chắn tương thích và bạn có thể kiểm tra tĩnh: ? 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ể trở thành một số nguyên khác, trong trường hợp đó thì hành vi không xác định.

Lưu ý rằng nếu kích thước thứ nguyên có tính động trong kết quả, thì không thể có hành vi không xác định. Trên thực tế, không có kích thước "dự kiến" do đó không thể có trường hợp không khớp.

Hình dạng không khớp cho các phép toán theo phần tử nhị phân

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
}

Khi nói đến các phép toán 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 nhau trong thời gian chạy. Tại thời điểm biên dịch, các phương diện tĩnh phải bằng nhau, nếu không, các phương diện này chỉ cần tương thích. Nếu bất kỳ phương diện nào là phương diện động trong đầu vào, thì có thể có hành vi không xác định trong 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 (là tĩnh hoặc động). Nếu tất cả dữ liệu đầu vào đều tĩnh, thì 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ỳ quy tắc ràng buộc nào.

Hình dạng không khớp đối với hoạt động nhận 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 trong 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ì bạn có thể xác minh điều này theo cách tĩnh. Nếu hình dạng kết quả hoàn toàn linh động, thì không thể có sự không khớp.