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 và %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 đó scale và zero_point được gọi là tham số lượng tử hoá. storage_min và storage_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) và 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 QuantizationStorageMin và QuantizationStorageMax để 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 scale và zero_point cho toàn bộ tensor hoặc có thể là trên mỗi trục, nghĩa là có nhiều scales và zero_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] và 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).
- (C12)
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> và 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
truevàfalse. - 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,32hoặc64). LoạisiNđã ký biểu thị giá trị số nguyên từ-2^(N-1)đến2^(N-1)-1, và loạiuiNkhông dấu biểu thị giá trị số nguyên từ0đến2^N-1. - Loại dấu phẩy động có thể là một trong những loại sau:
f8E3M4,f8E4M3vàf8E5M2số dấu phẩy động 8 bit tuân theo quy ước IEEE-754.- Các loại
f8E4M3FNvàf8E5M2tương ứng với các phương thức mã hoáE4M3vàE5M2của định dạng FP8 được mô tả trong phần Định dạng FP8 cho học sâu. - Các loại
f8E4M3FNUZvàf8E5M2FNUZtương ứng với mã hoáE4M3vàE5M2của các định dạng FP8 được mô tả trong phần Định dạng số 8 bit cho Mạng nơron sâu. - Loại
f8E4M3B11FNUZtương ứng với mã hoáE4M3của các định dạng FP8 được mô tả trong bài viết Đào tạo và suy luận theo điểm nổi kết hợp 8 bit (HFP8) cho mạng nơron sâu. - Loại
bf16tương ứng với định dạngbfloat16được mô tả trong BFloat16: Bí quyết đạt hiệu suất cao trên Cloud TPU. - Các loại
f16,f32vàf64tương ứng với các định dạngbinary16("độ chính xác đơn"),binary32("độ chính xác đơn") vàbinary64("độ chính xác kép") được mô tả trong tiêu chuẩn IEEE 754. - Loại
tf32tương ứng với định dạng TensorFloat32 và có hỗ trợ hạn chế trong StableHLO. - Các loại
f4E2M1FN,f6E2M3FN,f6E3M2FNvàf8E8M0FNUMX (chia tỷ lệ theo tỷ lệ) được mô tả trong bài viết Thông số kỹ thuật về định dạng vi mô OCP.
- Loại phức đại diện cho các giá trị phức có phần thực và phầ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ạif32) vàcomplex<f64>(cả hai phần đều thuộc loạif64).
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_indices và limit_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 và %init_value), 2 hàm đầu vào và 3 thuộc tính đầu vào (window_dimensions, window_strides và padding). 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 true và false. 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 và (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:
abstừ 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ếuis_complex(operand).baseline_element_type(operand).
Ví dụ
// %operand: [-2, 0, 2]
%result = "stablehlo.abs"(%operand) : (tensor<3xi32>) -> tensor<3xi32>
// %result: [2, 0, 2]
thêm
Ngữ nghĩa
Thực hiện phép cộng theo phần tử của hai tensor lhs và rhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với 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:
additiontừ 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).
- (C1)
- 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).
- (C2)
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]]
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
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ếuchannel_id <= 0 and use_global_device_ids = false.cross_replica_and_partition(replica_groups)nếuchannel_id > 0 and use_global_device_ids = false.flattened_ids(replica_groups)nếuchannel_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ảreceivertrongprocess_group.results...@process = concatenate(operands...@process, all_gather_dim)cho tất cảprocesstrongprocess_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_replicasnếu bạn sử dụngcross_replica.num_replicasnếu bạn sử dụngcross_replica_and_partition.num_processesnếu bạn sử dụngflattened_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]]
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ếuchannel_id <= 0 and use_global_device_ids = false.cross_replica_and_partition(replica_groups)nếuchannel_id > 0 and use_global_device_ids = false.flattened_ids(replica_groups)nếuchannel_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ânschedule, trong đó:exec(node)=computation(exec(node.left), exec(node.right)).exec(leaf)=leaf.value.
schedulelà 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_replicasnếu dùngcross_replica.num_replicasnếu bạn sử dụngcross_replica_and_partition.num_processesnếu bạn sử dụngflattened_ids.
- (C3)
0 <= replica_groups < size(replica_groups). - (C4) Nếu
use_global_device_ids = true, thìchannel_id > 0. - (C5)
computationcó 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]
all_to_all
Ngữ nghĩa
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ếuchannel_id <= 0.cross_partition(replica_groups)nếuchannel_id > 0.
Sau đó, trong mỗi process_group:
split_parts...@sender = split(operands...@sender, split_count, split_dimension)cho tất cảsendertrongprocess_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_replicasnếu dùngcross_replica.num_partitionsnếu dùngcross_partition.
- (C7)
0 <= replica_groups < size(replica_groups). - (C8)
dim(replica_groups, 1) = split_count. - (C9)
type(results...) = type(operands...)ngoại trừ, nếusplit_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]]
và
Ngữ nghĩa
Thực hiện phép AND theo phần tử của hai tensor lhs và rhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với 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]]
atan2
Ngữ nghĩa
Thực hiện phép toán atan2 theo phần tử trên tensor lhs và rhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
atan2từ 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]
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_scale và grad_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_scalevàgrad_offsetcó cùngbaseline_element_type. - (C3)
operand,grad_outputvàgrad_operandcó cùng hình dạng. - (C4)
scale,mean,variance,grad_scalevàgrad_offsetcó 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,variancevàresultcó cùngbaseline_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_mean và batch_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_varvàoutputcó cùngbaseline_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) và 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)và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').
- Nếu
- (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
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] = 0nếudim(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)vàzero_points(operand)có thể khác vớiquantization_dimension(result),scales(result)và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ả
dtrongaxes(operand):dim(operand, d) = 1hoặcdim(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]
// ]
// ]
ố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ếu0 <= 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]
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]
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]
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 lower là true) hoặc tam giác trên (nếu lower là false).
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]
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ếuchannel_id <= 0.cross_partition(replica_groups)nếuchannel_id > 0.
Sau đó, result@process được xác định bằng:
operand@process_groups[i, 0]nếu tồn tại mộtisao cho quá trình này nằm trongprocess_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 < Ntrong đóNđược xác định là:num_replicasnếu bạn sử dụngcross_replica.num_partitionsnếu bạn sử dụngcross_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ếuchannel_id <= 0.cross_partition(source_target_pairs)nếuchannel_id > 0.
Sau đó, result@process được xác định bằng:
operand@process_groups[i, 0], nếu tồn tại mộtisao choprocess_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_replicasnếu bạn sử dụngcross_replica.num_partitionsnếu dùngcross_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]]
so sánh
Ngữ nghĩa
Thực hiện so sánh theo phần tử của các tensor lhs và rhs theo comparison_direction và compare_type, đồng thời tạo ra một tensor result.
Các giá trị của comparison_direction và compare_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ử totalOrder và compareQuietEqual 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_direction và compare_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_direction là GE, 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, LE và LT |
|
| (I4) | compare_type |
enum của FLOAT, TOTALORDER, SIGNED và UNSIGNED |
(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à:SIGNEDnếuis_signed_integer(element_type(lhs)).UNSIGNEDnếuis_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs)).FLOAThoặcTOTALORDERnếuis_float(element_type(lhs)).FLOATnếuis_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]
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, lhs và rhs, đồ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ạicomplex<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)]
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 inputs và composite_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>
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 đó:
id = d0 + ... + dk-1 + kd.dbằngdimensionvàd0, ... là kích thước phương diện thứdcủainputs.
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]]
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]]
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)]
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ừ lhs và rhs bằng một ví dụ cụ thể.
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 = 1 và batch_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, HIGH và HIGHEST |
(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_countnếuresult_dim = output_batch_dimension.dim(rhs, kernel_output_feature_dimension)nếuresult_dim = output_feature_dimension.num_windowsnế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).
- (C27)
- 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).
- (C28)
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]]
// ]]
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:
costừ 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]]
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]]
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 inputs và called_computations và tạo ra results. Bạn có thể dùng has_side_effect, backend_config và api_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:
divisiontừ 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]
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_indextrong đósize(result_batching_index) = size(lhs_batching_dimensions),size(result_lhs_index) = size(lhs_result_dimensions)và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_typevàrhs_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_countvànum_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ành1.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, HIGH và HIGHEST |
(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).
- (C13)
- 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ó trongrhs_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).
- (C14)
- 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.
- (C21)
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]]
// ]
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)vàzero_points(operand)có thể khác vớiquantization_dimension(result),scales(result)và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ả
dtrongaxes(operand):dim(operand, d) = 1hoặcdim(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]
// ]
// ]
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, HIGH và HIGHEST |
(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_countnếuresult_dim = output_batch_dimension.dim(rhs, kernel_output_feature_dimension)nếuresult_dim = output_feature_dimension.num_windowsnế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).
- (C27)
- 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).
- (C28)
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]]
// ]]
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ủastart_indicestương ứng vớiindex_vector_dimkhông được đưa vào.offset_dim_sizes = shape(slice_sizes)ngoại trừ kích thước phương diện trongslice_sizestương ứng vớicollapsed_slice_dimssẽ không được đưa vào.combineđặtbatch_dim_sizestại các trục tương ứng vớibatch_dimsvàoffset_dim_sizestại các trục tương ứng vớioffset_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]]
// ]
// ]
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]
// ]
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_high và interior_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]
// ]
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)và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]]
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]
// ]
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ếu0 <= 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]
// ]
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:
exptừ 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]]
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:
expm1từ 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]
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_resulttrong đó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, RFFT và IRFFT |
(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ử
operandvàresultkhác nhau:- Nếu
fft_type = FFT,element_type(operand)vàelement_type(result)có cùng một loại phức tạp. - Nếu
fft_type = IFFT,element_type(operand)và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.
- Nếu
- (C3)
1 <= size(fft_length) <= 3. - (C4) Nếu trong số
operandvàresult, có một tensorrealthuộ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.
- Nếu
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]
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.
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 đóbilà các phần tử riêng lẻ trongbatch_indexvà:được chèn vào chỉ mụcindex_vector_dim, nếuindex_vector_dim<rank(start_indices).[start_indices[batch_index]].
- Đối với
d_operandtrongaxes(operand),full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand])nếud_operand = start_index_map[d_start].full_start_index[d_operand] = 0nếu không.
- Đối với
d_operandtrongaxes(operand),full_batching_index[d_operand] = batch_index[d_start - (d_start < index_vector_dim ? 0 : 1)]nếud_operand = operand_batching_dims[i_batching]và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 đóoilà các phần tử riêng lẻ trongoffset_indexvà0được chèn vào các chỉ mục từcollapsed_slice_dimsvàoperand_batching_dims.operand_index = full_start_index + full_batching_index + full_offset_index.
Nếu indices_are_sorted là true 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ủastart_indicestương ứng vớiindex_vector_dimkhông được đưa vào.offset_dim_sizes = slice_sizesngoại trừ kích thước phương diện trongslice_sizestương ứng vớicollapsed_slice_dimsvàoperand_batching_dimskhông được đưa vào.combineđặtbatch_dim_sizestại các trục tương ứng vớibatch_dimsvàoffset_dim_sizestại các trục tương ứng vớioffset_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]]
// ]
// ]
// ]
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
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]
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
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ếuis_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]
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ặcis_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]]
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]
// ]
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]
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:
logtừ 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]]
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:
logp1từ 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]
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]]
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)
computationcó loại(tensor<E0>, ..., tensor<EN-1>) -> tensor<E'>, trong đóEi = element_type(inputs[i])và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]]
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 lhs và rhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với boolean: logic OR.
- Đối với số nguyên: số nguyên lớn nhất.
- Đối với số thực:
maximumtừ 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]]
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 lhs và rhs 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:
minimumtừ 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]]
nhân
Ngữ nghĩa
Thực hiện tích theo phần tử của hai tensor lhs và rhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với boolean: logic AND.
- Đối với số nguyên: nhân số nguyên.
- Đối với số thực:
multiplicationtừ 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]]
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:
negatetừ 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]
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]
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
hoặc
Ngữ nghĩa
Thực hiện thao tác OR theo phần tử của hai tensor lhs và rhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với 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]]
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
đệ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_low và edge_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ếuresult_index = edge_padding_low + operand_index * (interior_padding + 1).padding_valuenế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]
// ]
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>
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ô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:
powtừ 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]
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ếuis_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]
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_transfer là true, 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_DEVICE và HOST_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_DEVICEnếuis_host_transfer = true,DEVICE_TO_DEVICEnếu không.
- (C2)
0 < size(results). - (C3)
is_empty(result[:-1])hoặcis_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)
giảm
Ngữ nghĩa
Áp dụng hàm rút gọn body cho inputs và init_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à body và init_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àodimensions.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ânscheduletrong đó:exec(node) = body(exec(node.left), exec(node.right)).exec(leaf) = leaf.value.
schedulelà 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ảindextrongindex_space(input_slices_converted)theo thứ tự từ điển tăng dần củaindex. - Được xen kẽ với một lượng
init_values_converteddo quá trình triển khai xác định ở các vị trí do quá trình triển khai xác định.
- Giá trị
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)
bodycó 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ủainputs...tương ứng vớidimensionskhông được đưa vào. - (C8)
element_type(results[i]) = Eicho tất cảitrong[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]
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_bits và mantissa_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_bitsbằng cách sử dụng ngữ nghĩaroundToIntegralTiesToEven. - Sau đó, nếu
mantissa_bitsnhỏ 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ànhmantissa_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_bitscung 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]
reduce_scatter
Ngữ nghĩa
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ếuchannel_id <= 0 and use_global_device_ids = false.cross_replica_and_partition(replica_groups)nếuchannel_id > 0 and use_global_device_ids = false.flattened_ids(replica_groups)nếuchannel_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ảsendertrongprocess_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_replicasnếu bạn sử dụngcross_replica.num_replicasnếu bạn sử dụngcross_replica_and_partition.num_processesnếu dùngflattened_ids.
- (C5)
0 <= replica_groups < size(replica_groups). - (C6) Nếu
use_global_device_ids = true, thìchannel_id > 0. - (C7)
computationcó 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]]
reduce_window
Ngữ nghĩa
Áp dụng hàm rút gọn body cho các cửa sổ của inputs và init_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ể.
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)
bodycó 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_windowstrong đó: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]) = Eicho tất cảitrong[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]]
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ònroundTowardZero. - Đố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]
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>
đổ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_index và operand_index có cùng vị trí trong thứ tự từ điển của index_space(result) và 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)và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]]
đả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] - 1nếudtrongdimensions.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]]
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 UNIFORM và NORMAL |
(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_FRY và PHILOX |
(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. 2nếurng_algorithm = THREE_FRY.2hoặc3nếurng_algorithm = PHILOX.
- do phương thức triển khai xác định nếu
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]
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]
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:
rSqrttừ 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]]
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.
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 đósilà các phần tử riêng lẻ trongupdate_scatter_indexvà:được chèn vào chỉ mụcindex_vector_dim, nếuindex_vector_dim<rank(scatter_indices).[scatter_indices[update_scatter_index]]nếu không.
- Đối với
d_inputtrongaxes(inputs[0]),full_start_index[d_input] = start_index[d_start]nếud_input = scatter_dims_to_operand_dims[d_start].full_start_index[d_input] = 0nếu không.
- Đối với
d_inputtrongaxes(inputs[0]),full_batching_index[d_input] = update_scatter_index[d_start - (d_start < index_vector_dim ? 0 : 1)]nếud_input = input_batching_dims[i_batching]vàd_start = scatter_indices_batching_dims[i_batching].full_batching_index[d_input] = 0nếu không.
update_window_index = update_index[update_window_dims...].full_window_index = [wi0, ..., 0, ..., wiN]trong đówilà các phần tử riêng lẻ trongupdate_window_indexvà0được chèn vào các chỉ mục từinserted_window_dimsvàinput_batching_dims.result_index = full_start_index + full_batching_index + full_window_index.
Do đó, results = exec(schedule, inputs), trong đó:
schedulelà một hoán vị do quá trình triển khai xác định củaindex_space(updates[0]).exec([update_index, ...], results) = exec([...], updated_results)trong đó:- Nếu
result_indexnằm trong giới hạn củashape(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_resultslà bản sao củaresultsvớiresults...[result_index]được đặt thànhupdated_values....- Hoặc
updated_results = results.
- Nếu
exec([], results) = results.
Nếu indices_are_sorted là true 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_indices là true 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_indices là true 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ủascatter_indicestương ứng vớiindex_vector_dimsẽ 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 tronginputs[0]tương ứng vớiinserted_window_dimsvàinput_batching_dims.combineđặtupdate_scatter_dim_sizestại các trục tương ứng vớiupdate_scatter_dimsvàupdate_window_dim_sizestại các trục tương ứng vớiupdate_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_computationcó 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]) = Eicho tất cảitrong[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]]
// ]
// ]
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]]
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ừ operand và source bằng một ví dụ cụ thể.
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_stridesvàpaddingđượ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)vàreduce_window_without_inithoạt động giống hệt nhưreduce_window, ngoại trừschedulecủareducecơ 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_indexnếuselected_values[source_index]có phần tửoperandtừ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_windowstrong đó: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)
selectcó loại(tensor<E>, tensor<E>) -> tensor<i1>trong đóE = element_type(operand). - (C10)
scattercó 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]]
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_transfer là true, 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_DEVICE và DEVICE_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_HOSTnếuis_host_transfer = true,DEVICE_TO_DEVICEnế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
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]
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]
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]
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]
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:
sintừ 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]]
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]
// ]
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ử e1 và e2 đượ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 đóriNlà các phần tử riêng lẻ trongresult_indexvà:được chèn vàoadjusted_dimension.inputs_together = (inputs[0]..., ..., inputs[N-1]...).results_together[result_slice] = sort(inputs_together[result_slice], comparator_together).- trong đó
sortsắ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ằngcomparator_togethersẽ trả vềtruenế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)
comparatorcó 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]]
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:
squareRoottừ 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]]
trừ
Ngữ nghĩa
Thực hiện phép trừ từng phần tử của hai tensor lhs và rhs 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:
subtractiontừ 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]]
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:
tantừ 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]
// ]
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:
tanhtừ 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]
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)vàquantization_dimension(result)có thể khác nhau, nếu không.
- (C2)
permutationlà một phép hoán vị củarange(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]]
// ]
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 a và b, result[i0, ..., iR-3, :, :] là giải pháp cho op(a[i0, ..., iR-3, :, :]) * x = b[i0, ..., iR-3, :, :] khi left_side là true hoặc x * op(a[i0, ..., iR-3, :, :]) = b[i0, ..., iR-3, :, :] khi left_side là false, 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ụnganhư hiện có.TRANSPOSE: Thực hiện phép toán trên phép chuyển đổi củaa.ADJOINT: Thực hiện thao tác hoán vị liên hợp củaa.
Dữ liệu đầu vào chỉ được đọc từ tam giác dưới của a, nếu lower là true 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, TRANSPOSE và ADJOINT |
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)và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)
resultcó loạituple<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))
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)
condcó loại(T0, ..., TN-1) -> tensor<i1>, trong đóTi = type(operand[i]). - (C2)
bodycó 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
xor
Ngữ nghĩa
Thực hiện phép XOR theo phần tử của hai tensor lhs và rhs, đồ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]]
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,complexso sánh #560 và tích chậpwindow_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 → %2 → return và
%1 → %0 → %2 → return.
func.func @main() -> tensor<f64> {
%0 = stablehlo.constant dense<1.0> : tensor<f64>
%1 = stablehlo.constant dense<2.0> : tensor<f64>
%2 = stablehlo.add %0, %1 : tensor<f64>
return %2 : tensor<f64>
}
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) và 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_id và partition_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_permute và
reduce_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]] và 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]] và 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]] và 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 = 4 và num_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 và %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, rhs và rhs_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ả 0 và rank(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, operand và result 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ầnTensorElementTypehoặcQuantizedTensorElementTypecủaTensorTypehoặcQuantizedTensorTypetươ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) -> Valuelà lối tắt chois_quantized(x) and quantization_dimension(x) is not None.is_per_tensor_quantized(x: Value | Placeholder | Type) -> Valuelà một lối tắt chois_quantized(x) and quantization_dimension(x) is None.is_promotable(x: Type, y: Type) -> boolkiểm tra xem có thể nâng cấp loạixlên loạiyhay không. KhixvàylàQuantizedTensorElementType, chương trình khuyến mãi chỉ được áp dụng chostorage_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) -> Valuelà lối tắt chois_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ềtruenếuxlàFloatType. Nếuxlà một giá trị hoặc phần giữ chỗ, thì hàm này là lối tắt chois_type_name(type(x)).max_value(x: Type) -> Valuetrả về giá trị lớn nhất của mộtTensorElementType. Nếuxkhông phải làTensorElementType, hãy trả vềNone.min_value(x: Type) -> Valuetrả về giá trị tối thiểu có thể của mộtTensorElementType. Nếuxkhô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ênmember_namethuộc mọi loại. Ví dụ:tensor_element_type(x)trả về phầnTensorElementTypecủaTensorTypetương ứng. Nếuxlà một giá trị hoặc phần giữ chỗ, thì hàm này sẽ là lối tắt chomember_name(type(x)). Nếuxkhô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ànhNonehay 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ị tensorlhsvàrhsrồi trả về kết quả đánh giá toán tửaddvớ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ủaxdựa trêntype(x)vàdestination_typenhư 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_quantize và uniform_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ềtruenếu tất cả các phần tử củaxlàNaNhoặcfalse. Nếuxkhô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ềtruenếu các phần tử củaxđượ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ếuxkhô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ềtruenếuxkhông có phần tử trùng lặp hoặcfalsenếu không. Nếuxkhô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ầnmember_namecủa tất cả các giá trị. Ví dụ:real_part(x)trả về phầnRealPartcủa mộtComplexConstanttương ứng. Nếuxkhô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ềtruenếu tất cả các phần tử củaxđều bằng nhau hoặcfalsenế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ếuxkhô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ắtnum_resultscủaxdọc theo trụcaxis. Nếuxkhông phải là tensor hoặcdim(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ềtruenếuxlà 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ềtruenếuxlà 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) -> Valuelà lối tắt chorange(rank(x)).dim(x: Value | Placeholder | Type, axis: Value) -> Valuelà lối tắt choshape(x)[axis].dims(x: Value | Placeholder | Type, axes: List) -> Listlà lối tắt cholist(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ụcsize(x)choTensorTypetươ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ếuxkhô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) -> Valuelà lối tắt chosize(shape(x)).shape(x: Value | Placeholder | Type) -> Valueđược xác định trong phần "Hàm có trên các kiểu" quamember_name.size(x: Value | Placeholder | Type) -> Valuelà lối tắt choreduce(lambda x, y: x * y, shape(x)).
Tính toán lượng tử hoá
def baseline_element_type(x: Value | Placeholder | Type) -> Typelà một lối tắt choelement_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_maxvàquantization_dimension(đối với loại lượng tử hoá trên mỗi trục) phải khớp nhau, nhưngscalesvàzero pointscó 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_opdù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.