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
true
và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
,32
hoặc64
). LoạisiN
đã ký biểu thị giá trị số nguyên từ-2^(N-1)
đến2^(N-1)-1
, và loạiuiN
khô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
,f8E4M3
vàf8E5M2
số dấu phẩy động 8 bit tuân theo quy ước IEEE-754.- Các loại
f8E4M3FN
vàf8E5M2
tương ứng với các phương thức mã hoáE4M3
vàE5M2
của định dạng FP8 được mô tả trong phần Định dạng FP8 cho học sâu. - Các loại
f8E4M3FNUZ
vàf8E5M2FNUZ
tương ứng với mã hoáE4M3
vàE5M2
củ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
f8E4M3B11FNUZ
tương ứng với mã hoáE4M3
củ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
bf16
tươ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
,f32
vàf64
tươ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
tf32
tương ứng với định dạng TensorFloat32 và có hỗ trợ hạn chế trong StableHLO. - Các loại
f4E2M1FN
,f6E2M3FN
,f6E3M2FN
vàf8E8M0FNU
MX (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:
abs
từ IEEE-754. - Đối với số phức: mô-đun số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(abs, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của số nguyên có dấu, dấu phẩy động hoặc kiểu phức hoặc tensor lượng tử hoá mỗi tensor | (C1-C2) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor của loại số nguyên có dấu hoặc dấu phẩy động hoặc tensor lượng tử hoá mỗi tensor | (C1–C2) |
Giới hạn
- (C1)
shape(result) = shape(operand)
. - (C2)
baseline_element_type(result)
được xác định là:complex_element_type(element_type(operand))
nế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:
addition
từ IEEE-754. - Đối với số phức: phép cộng số phức.
- Đối với các kiểu lượng tử hoá:
dequantize_op_quantize(add, lhs, rhs, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | lhs |
tensor hoặc tensor lượng tử hoá | (C1-C6) |
(I2) | rhs |
tensor hoặc tensor lượng tử hoá | (C1-C5), (C7) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor hoặc tensor lượng tử hoá | (C1-C7) |
Giới hạn
- Nếu toán tử sử dụng tensor không được lượng tử hoá:
- (C1)
type(lhs) = type(rhs) = type(result)
.
- (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ảreceiver
trongprocess_group
.results...@process = concatenate(operands...@process, all_gather_dim)
cho tất cảprocess
trongprocess_group
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operands |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C6) |
(I2) | all_gather_dim |
hằng số thuộc loại si64 |
(C1), (C6) |
(I3) | replica_groups |
Hằng số tensor 2 chiều thuộc loại si64 |
(C2-C4) |
(I4) | channel_id |
hằng số thuộc loại si64 |
(C5) |
(I5) | use_global_device_ids |
hằng số thuộc loại i1 |
(C5) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C6) |
Giới hạn
- (C1)
0 <= all_gather_dim < rank(operands...)
. - (C2)
is_unique(replica_groups)
. - (C3)
size(replica_groups)
được xác định là:num_replicas
nếu bạn sử dụngcross_replica
.num_replicas
nếu bạn sử dụngcross_replica_and_partition
.num_processes
nế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
.
schedule
là một cây nhị phân được xác định bằng cách triển khai, trong đó hoạt động duyệt theo thứ tự làto_destination_type(operands...@process_group...[result_index], type(func_inputs(computation)[0]))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operands |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C5), (C6) |
(I2) | replica_groups |
số đại lượng của hằng số tensor 1 chiều thuộc loại si64 |
(C1-C3) |
(I3) | channel_id |
hằng số thuộc loại si64 |
(C4) |
(I4) | use_global_device_ids |
hằng số thuộc loại i1 |
(C4) |
(I5) | computation |
hàm | (C5) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C6-C7) |
Giới hạn
- (C1)
is_unique(replica_groups)
. - (C2)
size(replica_groups)
được xác định là:num_replicas
nếu dùngcross_replica
.num_replicas
nếu bạn sử dụngcross_replica_and_partition
.num_processes
nế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)
computation
có loại(tensor<E>, tensor<E>) -> (tensor<E>)
trong đóis_promotable(element_type(operand), E)
. - (C6)
shape(results...) = shape(operands...)
. - (C7)
element_type(results...) = E
.
Ví dụ
// num_replicas: 2
// num_partitions: 1
// %operand0@(0, 0): [1, 2, 3, 4]
// %operand0@(1, 0): [5, 6, 7, 8]
// %operand1@(0, 0): [9, 10, 11, 12]
// %operand1@(1, 0): [13, 14, 15, 16]
%result:2 = "stablehlo.all_reduce"(%operand0, %operand0) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
// channel_id = 0
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
// use_global_device_ids = false
} : (tensor<4xi64>, tensor<4xi64>) -> (tensor<4xi64>, tensor<4xi64>)
// %result0@(0, 0): [6, 8, 10, 12]
// %result0@(1, 0): [6, 8, 10, 12]
// %result1@(0, 0): [22, 24, 26, 28]
// %result1@(1, 0): [22, 24, 26, 28]
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ảsender
trongprocess_group
.scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group]
trong đóreceiver_index = process_group.index(receiver)
.results...@process = concatenate(scattered_parts...@process, concat_dimension)
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operands |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C1–C3), (C9) |
(I2) | split_dimension |
hằng số thuộc loại si64 |
(C1), (C2), (C9) |
(I3) | concat_dimension |
hằng số loại si64 |
(C3), (C9) |
(I4) | split_count |
hằng số thuộc loại si64 |
(C2), (C4), (C8), (C9) |
(I5) | replica_groups |
Hằng số tensor 2 chiều thuộc loại si64 |
(C5-C8) |
(I6) | channel_id |
hằng số thuộc loại si64 |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C9) |
Giới hạn
- (C1)
0 <= split_dimension < rank(operands...)
. - (C2)
dim(operands..., split_dimension) % split_count = 0
. - (C3)
0 <= concat_dimension < rank(operands...)
. - (C4)
0 < split_count
. - (C5)
is_unique(replica_groups)
. - (C6)
size(replica_groups)
được xác định là:num_replicas
nếu dùngcross_replica
.num_partitions
nế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:
atan2
từ IEEE-754. - Đối với số phức: atan2 phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(atan2, lhs, rhs, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | lhs |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
(I2) | rhs |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
Ví dụ
// %lhs: [0.0, 1.0, -1.0]
// %rhs: [0.0, 0.0, 0.0]
%result = "stablehlo.atan2"(%lhs, %rhs) : (tensor<3xf64>, tensor<3xf64>) -> tensor<3xf64>
// %result: [0.0, 1.57079637, -1.57079637] // [0.0, pi/2, -pi/2]
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_scale
vàgrad_offset
có cùngbaseline_element_type
. - (C3)
operand
,grad_output
vàgrad_operand
có cùng hình dạng. - (C4)
scale
,mean
,variance
,grad_scale
vàgrad_offset
có cùng hình dạng. - (C5)
size(scale) = dim(operand, feature_index)
.
Ví dụ
// %operand: [
// [[1.0, 2.0], [3.0, 4.0]],
// [[3.0, 4.0], [1.0, 2.0]]
// ]
// %scale: [1.0, 1.0]
// %mean: [2.0, 3.0]
// %variance: [1.0, 1.0]
// %grad_output: [
// [[0.1, 0.1], [0.1, 0.1]],
// [[0.1, 0.1], [0.1, 0.1]]
// ]
%grad_operand, %grad_scale, %grad_offset =
"stablehlo.batch_norm_grad"(%operand, %scale, %mean, %variance, %grad_output) {
epsilon = 0.0 : f32,
feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>,
tensor<2x2x2xf64>) -> (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %grad_operand: [
// [[0.0, 0.0], [0.0, 0.0]],
// [[0.0, 0.0], [0.0, 0.0]]
// ]
// %grad_scale: [0.0, 0.0]
// %grad_offset: [0.4, 0.4]
batch_norm_inference
Ngữ nghĩa
Chuẩn hoá tensor operand
trên tất cả các phương diện ngoại trừ phương diện feature_index
và tạo ra một tensor result
. Theo cách chính thức hơn, thao tác này có thể được biểu thị dưới dạng một quá trình phân ly cho các thao tác StableHLO hiện có bằng cách sử dụng cú pháp Python như sau:
def batch_norm_inference(operand, scale, offset, mean, variance, epsilon, feature_index):
# Broadcast inputs to shape(operand)
scale_bcast = broadcast_in_dim(scale, [feature_index], type(operand))
offset_bcast = broadcast_in_dim(offset, [feature_index], type(operand))
mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
variance_bcast = broadcast_in_dim(variance, [feature_index], type(operand))
epsilon_bcast = broadcast_in_dim(constant(epsilon, element_type(operand)), [],
type(operand))
# Perform normalization using the provided `mean` and `variance` instead of
# computing them like `batch_norm_training` does.
centered_operand = subtract(operand, mean_bcast)
stddev = sqrt(add(variance_bcast, epsilon_bcast))
normalized_operand = divide(centered_operand, stddev)
return add(multiply(scale_bcast, normalized_operand), offset_bcast)
Đối với các loại được lượng tử hoá, hãy thực hiện dequantize_op_quantize(lambda operand, scale, offset, mean, variance:
batch_norm_inference(operand, scale, offset, mean, variance, epsilon,
feature_index), operand, scale, offset, mean, variance, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor loại dấu phẩy động hoặc tensor lượng tử hoá mỗi tensor | (C1-C7) |
(I2) | scale |
Tensor 1 chiều của loại dấu phẩy động hoặc lượng tử hoá theo tensor | (C2), (C3) |
(I3) | offset |
Tensor 1 chiều của loại lượng tử hoá theo dấu phẩy động hoặc mỗi tensor | (C2), (C4) |
(I4) | mean |
Tensor 1 chiều của loại dấu phẩy động hoặc lượng tử hoá theo tensor | (C5) |
(I5) | variance |
Tensor 1 chiều của loại dấu phẩy động hoặc lượng tử hoá theo tensor | (C2), (C6) |
(I6) | epsilon |
hằng số thuộc loại f32 |
|
(I7) | feature_index |
hằng số thuộc loại si64 |
(C1), (C3-C6) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc tensor lượng tử hoá trên mỗi tensor | (C2), (C7) |
Giới hạn
- (C1)
0 <= feature_index < rank(operand)
. - (C2)
operand
,scale
,offset
,mean
,variance
vàresult
có 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_var
vàoutput
có 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] = 0
nế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ả
d
trongaxes(operand)
:dim(operand, d) = 1
hoặ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ộti
sao 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 < N
trong đóN
được xác định là:num_replicas
nếu bạn sử dụngcross_replica
.num_partitions
nế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ộti
sao 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_replicas
nếu bạn sử dụngcross_replica
.num_partitions
nế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à:SIGNED
nếuis_signed_integer(element_type(lhs))
.UNSIGNED
nếuis_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs))
.FLOAT
hoặcTOTALORDER
nếuis_float(element_type(lhs))
.FLOAT
nế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
.d
bằngdimension
vàd0
, ... là kích thước phương diện thứd
củ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_count
nếuresult_dim = output_batch_dimension
.dim(rhs, kernel_output_feature_dimension)
nếuresult_dim = output_feature_dimension
.num_windows
nếu không, trong đó:output_spatial_dimensions[spatial_dim] = result_dim
.lhs_dim = input_spatial_dimensions[spatial_dim]
.rhs_dim = kernel_spatial_dimensions[spatial_dim]
.dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1
.padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1]
.dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1
.is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim]
.num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1
.
- (C26)
rank(result) = N
. - Nếu toán tử sử dụng tensor không được lượng tử hoá:
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result)
.
- (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:
cos
từ IEEE-754. - Đối với số phức: cosin phức.
- Đối với các kiểu lượng tử hoá:
dequantize_op_quantize(cosine, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.cosine"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[1.0, 0.0], [-1.0, 0.0]]
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:
division
từ IEEE-754. - Đối với số phức: phép chia số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(divide, lhs, rhs, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | lhs |
tensor số nguyên, dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
(I2) | rhs |
tensor số nguyên, dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor của số nguyên, dấu phẩy động hoặc kiểu phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
Ví dụ
// %lhs: [17.1, -17.1, 17.1, -17.1]
// %rhs: [3.0, 3.0, -3.0, -3.0]
%result = "stablehlo.divide"(%lhs, %rhs) : (tensor<4xf32>, tensor<4xf32>) -> tensor<4xf32>
// %result: [5.66666651, -5.66666651, -5.66666651, 5.66666651]
dot_general
Ngữ nghĩa
Tính toán tích vô hướng giữa các lát cắt của lhs
và các lát cắt của rhs
, đồng thời tạo ra một tensor result
.
Chính thức hơn, result[result_index] = dot_product
, trong đó:
lhs_result_dimensions = [d for d in axes(lhs) and d not in lhs_batching_dimensions and d not in lhs_contracting_dimensions]
.rhs_result_dimensions = [d for d in axes(rhs) and d not in rhs_batching_dimensions and d not in rhs_contracting_dimensions]
.result_batching_index + result_lhs_index + result_rhs_index = result_index
trong đósize(result_batching_index) = size(lhs_batching_dimensions)
,size(result_lhs_index) = size(lhs_result_dimensions)
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_type
và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_count
và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ả
d
trongaxes(operand)
:dim(operand, d) = 1
hoặ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_count
nếuresult_dim = output_batch_dimension
.dim(rhs, kernel_output_feature_dimension)
nếuresult_dim = output_feature_dimension
.num_windows
nếu không, trong đó:output_spatial_dimensions[spatial_dim] = result_dim
.lhs_dim = input_spatial_dimensions[spatial_dim]
.rhs_dim = kernel_spatial_dimensions[spatial_dim]
.dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1
.padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1]
.dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1
.is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim]
.num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1
.
- (C26)
rank(result) = N
. - Nếu toán tử sử dụng tensor không được lượng tử hoá:
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result)
.
- (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_indices
tương ứng vớiindex_vector_dim
không được đưa vào.offset_dim_sizes = shape(slice_sizes)
ngoại trừ kích thước phương diện trongslice_sizes
tương ứng vớicollapsed_slice_dims
sẽ không được đưa vào.combine
đặtbatch_dim_sizes
tại các trục tương ứng vớibatch_dims
vàoffset_dim_sizes
tạ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:
exp
từ IEEE-754. - Đối với số phức: số mũ phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(exponential, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.exponential"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[1.0, 2.7182818284590451], [7.3890560989306504, 20.085536923187668]]
exponential_minus_one
Ngữ nghĩa
Thực hiện phép toán mũ trừ một theo từng phần tử trên tensor operand
và tạo ra một tensor result
. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
expm1
từ IEEE-754. - Đối với số phức: số mũ phức trừ đi một.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(exponential_minus_one, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %operand: [0.0, 1.0]
%result = "stablehlo.exponential_minus_one"(%operand) : (tensor<2xf64>) -> tensor<2xf64>
// %result: [0.0, 1.71828187]
fft
Ngữ nghĩa
Thực hiện phép biến đổi Fourier thuận và nghịch cho đầu vào/đầu ra thực và phức.
fft_type
là một trong những giá trị sau:
FFT
: Chuyển tiếp FFT phức tạp sang phức tạp.IFFT
: FFT nghịch đảo từ phức tạp đến phức tạp.RFFT
: Chuyển tiếp FFT thực sang phức.IRFFT
: FFT nghịch đảo từ số thực đến số phức (tức là lấy số phức, trả về số thực).
Chính thức hơn, vì hàm fft
lấy tensor 1 chiều của các kiểu phức làm đầu vào, tạo ra các tensor 1 chiều có cùng loại với đầu ra và tính toán phép biến đổi Fourier riêng biệt:
Đối với fft_type = FFT
, result
được xác định là kết quả cuối cùng của một loạt các phép tính L trong đó L = size(fft_length)
. Ví dụ: đối với L = 3
:
result1[i0, ..., :] = fft(operand[i0, ..., :])
.result2[i0, ..., :, iR-1] = fft(result1[i0, ..., :, iR-1])
.result[i0, ..., :, iR-2, iR-1] = fft(result2[i0, ..., :, iR-2, iR-1])
.
Hơn nữa, với hàm ifft
có cùng chữ ký kiểu và tính toán hàm nghịch đảo của fft
:
Đối với fft_type = IFFT
, result
được xác định là giá trị nghịch đảo của phép tính cho fft_type = FFT
. Ví dụ: đối với L = 3
:
result1[i0, ..., :, iR-2, iR-1] = ifft(operand[i0, ..., :, iR-2, iR-1])
.result2[i0, ..., :, iR-1] = ifft(result1[i0, ..., :, iR-1])
.result[i0, ..., :] = ifft(result2[i0, ..., :])
.
Hơn nữa, với hàm rfft
dùng tensor 1 chiều của các kiểu dấu phẩy động, tạo ra tensor 1 chiều thuộc các kiểu phức tạp của cùng một ngữ nghĩa dấu phẩy động và hoạt động như sau:
rfft(real_operand) = truncated_result
trong đócomplex_operand... = (real_operand..., 0.0)
.complex_result = fft(complex_operand)
.truncated_result = complex_result[:(rank(complex_result) / 2 + 1)]
.
(Khi phép biến đổi Fourier rời rạc được tính toán cho các toán hạng thực, các phần tử N/2 + 1
đầu tiên của kết quả xác định rõ ràng phần còn lại của kết quả, vì vậy, kết quả của rfft
bị cắt bớt để tránh tính toán các phần tử thừa).
Đối với fft_type = RFFT
, result
được xác định là kết quả cuối cùng của một loạt các phép tính L trong đó L = size(fft_length)
. Ví dụ: đối với L = 3
:
result1[i0, ..., :] = rfft(operand[i0, ..., :])
.result2[i0, ..., :, iR-1] = fft(result1[i0, ..., :, iR-1])
.result[i0, ..., :, iR-2, iR-1] = fft(result2[i0, ..., :, iR-2, iR-1])
.
Cuối cùng, với hàm irfft
có cùng chữ ký kiểu và tính toán hàm nghịch đảo của rfft
:
Đối với fft_type = IRFFT
, result
được xác định là giá trị nghịch đảo của phép tính cho fft_type = RFFT
. Ví dụ: đối với L = 3
:
result1[i0, ..., :, iR-2, iR-1] = ifft(operand[i0, ..., :, iR-2, iR-1])
.result2[i0, ..., :, iR-1] = ifft(result1[i0, ..., :, iR-1])
.result[i0, ..., :] = irfft(result2[i0, ..., :])
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức | (C1), (C2), (C4), (C5) |
(I2) | fft_type |
enum của FFT , IFFT , 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ử
operand
vàresult
khá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ố
operand
vàresult
, có một tensorreal
thuộc loại dấu phẩy động, thìshape(real)[-size(fft_length):] = fft_length
. - (C5)
shape(result) = shape(operand)
ngoại trừ:- Nếu
fft_type = RFFT
,dim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1
. - Nếu
fft_type = IRFFT
,dim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1
.
- 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 đóbi
là các phần tử riêng lẻ trongbatch_index
và:
đượ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_operand
trongaxes(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] = 0
nếu không.
- Đối với
d_operand
trongaxes(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 đóoi
là các phần tử riêng lẻ trongoffset_index
và0
được chèn vào các chỉ mục từcollapsed_slice_dims
và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_indices
tương ứng vớiindex_vector_dim
không được đưa vào.offset_dim_sizes = slice_sizes
ngoại trừ kích thước phương diện trongslice_sizes
tương ứng vớicollapsed_slice_dims
vàoperand_batching_dims
không được đưa vào.combine
đặtbatch_dim_sizes
tại các trục tương ứng vớibatch_dims
vàoffset_dim_sizes
tạ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:
log
từ IEEE-754. - Đối với số phức: logarit phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(log, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %operand: [[1.0, 2.0], [3.0, 4.0]]
%result = "stablehlo.log"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.0, 0.69314718055994529], [1.0986122886681098, 1.3862943611198906]]
log_plus_one
Ngữ nghĩa
Thực hiện phép lôgarit theo phần tử cộng với một phép toán trên tensor operand
và tạo ra một tensor result
. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
logp1
từ IEEE-754. - Đối với số phức: lôgarit phức cộng 1.
- Đối với các kiểu được lượng tử hoá:
dequantize_op_quantize(log_plus_one, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %operand: [0.0, -0.999, 7.0, 6.38905621, 15.0]
%result = "stablehlo.log_plus_one"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [0.0, -6.90776825, 2.07944155, 2.0, 2.77258873]
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)
computation
có 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:
maximum
từ IEEE-754. - Đối với số phức: giá trị tối đa theo thứ tự bảng chữ cái cho cặp
(real, imaginary)
. Việc áp đặt thứ tự trên số phức có liên quan đến ngữ nghĩa đáng ngạc nhiên, vì vậy, trong tương lai, chúng tôi dự định sẽ xoá tính năng hỗ trợ số phức cho toán tử này (#560). - Đối với các loại được lượng tử hoá:
dequantize_op_quantize(maximum, lhs, rhs, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | lhs |
tensor hoặc tensor lượng tử hoá theo tensor | (C1) |
(I2) | rhs |
tensor hoặc tensor lượng tử hoá theo tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
Ví dụ
// %lhs: [[1, 2], [7, 8]]
// %rhs: [[5, 6], [3, 4]]
%result = "stablehlo.maximum"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 8]]
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:
minimum
từ IEEE-754. - Đối với số phức: giá trị tối thiểu trong từ điển cho cặp
(real, imaginary)
. Việc áp đặt thứ tự trên số phức có liên quan đến ngữ nghĩa đáng ngạc nhiên, vì vậy, trong tương lai, chúng tôi dự định sẽ xoá tính năng hỗ trợ số phức cho toán tử này (#560). - Đối với các loại được lượng tử hoá:
dequantize_op_quantize(minimum, lhs, rhs, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | lhs |
tensor hoặc tensor lượng tử hoá theo tensor | (C1) |
(I2) | rhs |
tensor hoặc tensor lượng tử hoá theo tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
Ví dụ
// %lhs: [[1, 2], [7, 8]]
// %rhs: [[5, 6], [3, 4]]
%result = "stablehlo.minimum"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[1, 2], [3, 4]]
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:
multiplication
từ IEEE-754. - Đối với số phức: phép nhân phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(multiply, lhs, rhs, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | lhs |
tensor hoặc tensor lượng tử hoá theo tensor | (C1) |
(I2) | rhs |
tensor hoặc tensor lượng tử hoá theo tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.multiply"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 12], [21, 32]]
phủ định
Ngữ nghĩa
Thực hiện phép phủ định các phần tử của tensor operand
và tạo ra tensor result
. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số nguyên đã ký: phủ định số nguyên.
- Đối với số nguyên không dấu: bitcast thành số nguyên có dấu, phủ định số nguyên, bitcast trở lại số nguyên không dấu.
- Đối với số thực:
negate
từ IEEE-754. - Đối với số phức: phủ định phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(negate, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor số nguyên, dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor của số nguyên, dấu phẩy động hoặc kiểu phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// Negation operation with integer Tensors
// %operand: [0, -2]
%result = "stablehlo.negate"(%operand) : (tensor<2xi32>) -> tensor<2xi32>
// %result: [0, 2]
// Negation operation with with complex tensors
// %operand: (2.5, 0.0)
%result = "stablehlo.negate"(%operand) : (tensor<1xcomplex<f32>>) -> tensor<1xcomplex<f32>>
// %result: [-2.5, -0.0]
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_value
nếu không.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor hoặc tensor lượng tử hoá theo tensor | (C1), (C2), (C4) |
(I2) | padding_value |
Tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
(I3) | edge_padding_low |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C4) |
(I4) | edge_padding_high |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C4) |
(I5) | interior_padding |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2-C4) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor hoặc tensor lượng tử hoá theo tensor | (C3-C6) |
Giới hạn
- (C1)
element_type(operand) = element_type(padding_value) = element_type(result)
. - (C2)
size(edge_padding_low) = size(edge_padding_high) = size(interior_padding) = rank(operand)
. - (C3)
0 <= interior_padding
. - (C4)
shape(result) = shape(operand) + edge_padding_low + max(shape(operand) - 1, 0) * interior_padding + edge_padding_high
.
Ví dụ
// %operand: [
// [1, 2, 3],
// [4, 5, 6]
// ]
// %padding_value: 0
%result = "stablehlo.pad"(%operand, %padding_value) {
edge_padding_low = array<i64: 0, 1>,
edge_padding_high = array<i64: 2, 1>,
interior_padding = array<i64: 1, 2>
} : (tensor<2x3xi32>, tensor<i32>) -> tensor<5x9xi32>
// %result: [
// [0, 1, 0, 0, 2, 0, 0, 3, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0],
// [0, 4, 0, 0, 5, 0, 0, 6, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0]
// ]
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:
pow
từ IEEE-754. - Đối với số phức: số mũ phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(power, lhs, rhs, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | lhs |
tensor số nguyên, dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
(I2) | rhs |
tensor số nguyên, dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor của số nguyên, dấu phẩy động hoặc kiểu phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %lhs: [-2.0, -0.0, -36.0, 5.0, 3.0, 10000.0]
// %rhs: [2.0, 2.0, 1.1, 2.0, -1.0, 10.0]
%result = "stablehlo.power"(%lhs, %rhs) : (tensor<6xf64>, tensor<6xf64>) -> tensor<6xf64>
// %result: [4.0, 0.0, -nan, 25.0, 0.333333343, inf]
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_DEVICE
nếuis_host_transfer = true
,DEVICE_TO_DEVICE
nế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ânschedule
trong đó:exec(node) = body(exec(node.left), exec(node.right))
.exec(leaf) = leaf.value
.
schedule
là một cây nhị phân đầy đủ được xác định bằng cách triển khai, trong đó truyền tải theo thứ tự bao gồm:- Giá trị
input_slices_converted...[index]
, cho tất cảindex
trongindex_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_converted
do quá trình triển khai xác định ở các vị trí do quá trình triển khai xác định.
- 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)
body
có loại(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,
tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)
trong đóis_promotable(element_type(inputs[i]), Ei)
. - (C7)
shape(results...) = shape(inputs...)
ngoại trừ kích thước phương diện củainputs...
tương ứng vớidimensions
không được đưa vào. - (C8)
element_type(results[i]) = Ei
cho tất cải
trong[0,N)
.
Ví dụ
// %input = [[0, 1, 2, 3, 4, 5]]
// %init_value = 0
%result = "stablehlo.reduce"(%input, %init_value) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
dimensions = array<i64: 1>
} : (tensor<1x6xi64>, tensor<i64>) -> tensor<1xi64>
// %result = [15]
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_bits
bằng cách sử dụng ngữ nghĩaroundToIntegralTiesToEven
. - Sau đó, nếu
mantissa_bits
nhỏ hơn số bit dấu phẩy động của giá trị ban đầu, thì các bit dấu phẩy động sẽ bị cắt bớt thà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_bits
cung cấp, thì kết quả trung gian sẽ tràn đến vô cùng bằng cách sử dụng dấu ban đầu hoặc sẽ tràn về 0 bằng dấu ban đầu. - Đối với các loại được lượng tử hoá, hãy thực hiện
dequantize_op_quantize( lambda operand: reduce_precision(operand, exponent_bits, mantissa_bits), operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor thuộc loại dấu phẩy động hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
(I2) | exponent_bits |
hằng số thuộc loại si32 |
(C2) |
(I3) | mantissa_bits |
hằng số thuộc loại si32 |
(C3) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
output |
tensor thuộc loại dấu phẩy động hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(output)
. - (C2)
1 <= exponent_bits
. - (C3)
0 <= mantissa_bits
.
Ví dụ
// Logical values: +Inf, NaN, +Denormal, 0.0, 65519.0, 65520.0
// %operand: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0x0000000000000001, 0.0, 65519.0, 65520.0]
%output = "stablehlo.reduce_precision"(%operand) {
exponent_bits = 5 : i32,
mantissa_bits = 10 : i32
} : (tensor<6xf64>) -> tensor<6xf64>
// Logical values: +Inf, NaN, 0.0, 0.0, 65504.0, +Inf
// %output: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0.0, 0.0, 65504.0, 0x7FF0000000000000]
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ảsender
trongprocess_group
, trong đóreceiver_index = process_group.index(receiver)
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor hoặc tensor lượng tử hoá theo tensor | (C1), (C2), (C7), (C8) |
(I2) | scatter_dimension |
hằng số loại si64 |
(C1), (C2), (C8) |
(I3) | replica_groups |
Hằng số tensor 2 chiều thuộc loại si64 |
(C3–C5) |
(I4) | channel_id |
hằng số thuộc loại si64 |
(C6) |
(I5) | use_global_device_ids |
hằng số loại i1 |
(C6) |
(I6) | computation |
hàm | (C7) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor hoặc tensor lượng tử hoá mỗi tensor | (C8-C9) |
Giới hạn
- (C1)
dim(operand, scatter_dimension) % dim(process_groups, 1) = 0
. - (C2)
0 <= scatter_dimension < rank(operand)
. - (C3)
is_unique(replica_groups)
. - (C4)
size(replica_groups)
được định nghĩa là:num_replicas
nếu bạn sử dụngcross_replica
.num_replicas
nếu bạn sử dụngcross_replica_and_partition
.num_processes
nếu dùngflattened_ids
.
- (C5)
0 <= replica_groups < size(replica_groups)
. - (C6) Nếu
use_global_device_ids = true
, thìchannel_id > 0
. - (C7)
computation
có loại(tensor<E>, tensor<E>) -> (tensor<E>)
trong đóis_promotable(element_type(operand), E)
. - (C8)
shape(result) = shape(operand)
ngoại trừ:dim(result, scatter_dimension) = dim(operand, scatter_dimension) / dim(process_groups, 1)
.
- (C9)
element_type(result) = E
.
Ví dụ
// num_replicas: 2
// num_partitions: 1
// %operand@(0, 0): [[1, 2, 3, 4],
// [5, 6, 7, 8]]
// %operand@(1, 0): [[9, 10, 11, 12],
// [13, 14, 15, 16]]
%result = "stablehlo.reduce_scatter"(%operand) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
scatter_dimension = 1 : i64,
replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor<2x4xi64>) -> tensor<2x2xi64>
//
// %result@(0, 0): [[10, 12],
// [18, 20]]
// %result@(1, 0): [[14, 16],
// [22, 24]]
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)
body
có loại(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,
tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)
trong đóis_promotable(element_type(inputs[i]), Ei)
. - (C14)
same(shape(results...))
. - (C15)
shape(results[0]) = num_windows
trong đó:dilated_input_shape = shape(inputs[0]) = 0 ? 0 : (shape(inputs[0]) - 1) * base_dilations + 1
.padded_input_shape = padding[:, 0] + dilated_input_shape + padding[:, 1]
.dilated_window_shape = (window_dimensions - 1) * window_dilations + 1
.is_empty_window = padded_input_shape = 0 || dilated_window_shape > padded_input_shape
.num_windows = is_empty_window ? 0 : floor((padded_input_shape - dilated_window_shape) / window_strides) + 1
.
- (C16)
element_type(results[i]) = Ei
cho tất cải
trong[0,N)
.
Ví dụ
// %input = [[1, 2], [3, 4], [5, 6]]
// %init_value = 0
%result = "stablehlo.reduce_window"(%input, %init_value) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
window_dimensions = array<i64: 2, 1>,
window_strides = array<i64: 4, 1>,
base_dilations = array<i64: 2, 1>,
window_dilations = array<i64: 3, 1>,
padding = dense<[[2, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<3x2xi64>, tensor<i64>) -> tensor<2x2xi64>
// %result = [[0, 0], [3, 4]]
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] - 1
nếud
trongdimensions
.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
. 2
nếurng_algorithm = THREE_FRY
.2
hoặc3
nế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:
rSqrt
từ IEEE-754. - Đối với số phức: căn bậc hai nghịch đảo phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(rsqrt, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %operand: [[1.0, 4.0], [9.0, 25.0]]
%result = "stablehlo.rsqrt"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[1.0, 0.5], [0.33333343, 0.2]]
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 đósi
là các phần tử riêng lẻ trongupdate_scatter_index
và:
đượ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_input
trongaxes(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] = 0
nếu không.
- Đối với
d_input
trongaxes(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] = 0
nếu không.
update_window_index = update_index[update_window_dims...]
.full_window_index = [wi0, ..., 0, ..., wiN]
trong đówi
là các phần tử riêng lẻ trongupdate_window_index
và0
được chèn vào các chỉ mục từinserted_window_dims
vàinput_batching_dims
.result_index = full_start_index + full_batching_index + full_window_index
.
Do đó, results = exec(schedule, inputs)
, trong đó:
schedule
là một hoán vị do quá trình triển khai xác định củaindex_space(updates[0])
.exec([update_index, ...], results) = exec([...], updated_results)
trong đó:- Nếu
result_index
nằ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_results
là bản sao củaresults
vớ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_indices
tương ứng vớiindex_vector_dim
sẽ không được đưa vào.update_window_dim_sizes <= shape(inputs[0])
ngoại trừ việc không bao gồm các kích thước kích thước tronginputs[0]
tương ứng vớiinserted_window_dims
vàinput_batching_dims
.combine
đặtupdate_scatter_dim_sizes
tại các trục tương ứng vớiupdate_scatter_dims
vàupdate_window_dim_sizes
tạ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_computation
có kiểu(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)
, trong đóis_promotable(element_type(inputs[i]), Ei)
. - (C24)
shape(inputs...) = shape(results...)
. - (C25)
element_type(results[i]) = Ei
cho tất cải
trong[0,N)
.
Ví dụ
// %input: [
// [
// [[1, 2], [3, 4], [5, 6], [7, 8]],
// [[9, 10],[11, 12], [13, 14], [15, 16]],
// [[17, 18], [19, 20], [21, 22], [23, 24]]
// ],
// [
// [[25, 26], [27, 28], [29, 30], [31, 32]],
// [[33, 34], [35, 36], [37, 38], [39, 40]],
// [[41, 42], [43, 44], [45, 46], [47, 48]]
// ]
// ]
// %scatter_indices: [
// [
// [[0, 0], [1, 0], [2, 1]],
// [[0, 1], [1, 1], [0, 9]]
// ],
// [
// [[0, 0], [2, 1], [2, 2]],
// [[1, 2], [0, 1], [1, 0]]
// ]
// ]
// %update: [
// [
// [[1, 1], [1, 1], [1, 1]],
// [[1, 1], [1, 1], [1, 1]]
// ],
// [
// [[1, 1], [1, 1], [1, 1]],
// [[1, 1], [1, 1], [1, 1]]
// ]
// ]
%result = "stablehlo.scatter"(%input, %scatter_indices, %update) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
scatter_dimension_numbers = #stablehlo.scatter<
update_window_dims = [3, 4],
inserted_window_dims = [1],
input_batching_dims = [0],
scatter_indices_batching_dims = [1],
scatter_dims_to_operand_dims = [2, 1],
index_vector_dim = 3>,
indices_are_sorted = false,
unique_indices = false
} : (tensor<2x3x4x2xi64>, tensor<2x2x3x2xi64>, tensor<2x2x3x2x2xi64>) -> tensor<2x3x4x2xi64>
// %result: [
// [
// [[3, 4], [6, 7], [6, 7], [7, 8]],
// [[9, 10],[11, 12], [15, 16], [17, 18]],
// [[17, 18], [19, 20], [22, 23], [24, 25]]
// ],
// [
// [[25, 26], [28, 29], [30, 31], [31, 32]],
// [[35, 36], [38, 39], [38, 39], [39, 40]],
// [[41, 42], [44, 45], [46, 47], [47, 48]]
// ]
// ]
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_strides
và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_init
hoạt động giống hệt nhưreduce_window
, ngoại trừschedule
củareduce
cơ bản (xem reduce) không bao gồm các giá trị khởi tạo. Hiện tại, chưa xác định được điều gì sẽ xảy ra nếu cửa sổ tương ứng không có giá trị (#731).result[result_index] = reduce([source_values], [init_value], [0], scatter)
trong đó:source_values = [source[source_index] for source_index in source_indices]
.selected_index(source_index) = operand_index
nếuselected_values[source_index]
có phần tửoperand
từoperand_index
.source_indices = [source_index for source_index in indices(source) if selected_index(source_index) = result_index]
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor hoặc tensor lượng tử hoá theo tensor | (C1-C4), (C6), (C8-C11) |
(I2) | source |
tensor hoặc tensor lượng tử hoá theo tensor | (C1), (C2) |
(I3) | init_value |
Tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor | (C3) |
(I4) | window_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C4), (C5) |
(I5) | window_strides |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C6), (C7) |
(I6) | padding |
Hằng số tensor 2 chiều thuộc loại si64 |
(C2), (C8) |
(I7) | select |
hàm | (C9) |
(I8) | scatter |
hàm | (C10) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor hoặc tensor lượng tử hoá theo tensor | (C11-C12) |
Giới hạn
- (C1)
element_type(operand) = element_type(source)
. - (C2)
shape(source) = num_windows
trong đó:padded_operand_shape = padding[:, 0] + shape(operand) + padding[:, 1]
.is_empty_window = padded_operand_shape = 0 || window_dimensions > padded_operand_shape
.num_windows = is_empty_window ? 0 : floor((padded_operand_shape - window_dimensions) / window_strides) + 1
.
- (C3)
element_type(init_value) = element_type(operand)
. - (C4)
size(window_dimensions) = rank(operand)
. - (C5)
0 < window_dimensions
. - (C6)
size(window_strides) = rank(operand)
. - (C7)
0 < window_strides
. - (C8)
shape(padding) = [rank(operand), 2]
. - (C9)
select
có loại(tensor<E>, tensor<E>) -> tensor<i1>
trong đóE = element_type(operand)
. - (C10)
scatter
có kiểu(tensor<E>, tensor<E>) -> tensor<E>
trong đóis_promotable(element_type(operand), E)
. - (C11)
shape(operand) = shape(result)
. - (C12)
element_type(result) = E
.
Ví dụ
// %operand: [[1, 5], [2, 5], [3, 6], [4, 4]]
// %source: [[5, 6], [7, 8]]
// %init_value: 0
%result = "stablehlo.select_and_scatter"(%operand, %source, %init_value) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.compare"(%arg0, %arg1) {
comparison_direction = #stablehlo<comparison_direction GE>
} : (tensor<i64>, tensor<i64>) -> tensor<i1>
"stablehlo.return"(%0) : (tensor<i1>) -> ()
}, {
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
window_dimensions = array<i64: 3, 1>,
window_strides = array<i64: 2, 1>,
padding = dense<[[0, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<4x2xi64>, tensor<2x2xi64>, tensor<i64>) -> tensor<4x2xi64>
// %result: [[0, 0], [0, 0], [5, 14], [7, 0]]
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_HOST
nếuis_host_transfer = true
,DEVICE_TO_DEVICE
nếu không.
Ví dụ
%result = "stablehlo.send"(%operand, %token) {
channel_handle = #stablehlo.channel_handle<handle = 1, type = 2>,
is_host_transfer = true
} : (tensor<2x2xi64>, !stablehlo.token) -> !stablehlo.token
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:
sin
từ IEEE-754. - Đối với số phức: sin phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(sine, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.sine"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [0.0, -1.0]]
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 đóriN
là các phần tử riêng lẻ trongresult_index
và:
đượ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 đó
sort
sắp xếp một lát cắt 1 chiều theo thứ tự không giảm dần, dự kiến rằngcomparator_together
sẽ trả vềtrue
nếu đối số bên trái nhỏ hơn đối số thứ hai bên phải. def comparator_together(lhs_together, rhs_together): args = [] for (lhs_el, rhs_el) in zip(lhs_together, rhs_together): args.append(lhs_el) args.append(rhs_el) return comparator(*args)
(results[0]..., ..., results[N-1]...) = results_together
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | inputs |
số biến thiên của tensor hoặc tensor lượng tử hoá mỗi tensor | (C1–C5) |
(I2) | dimension |
hằng số loại si64 |
(C4) |
(I3) | is_stable |
hằng số thuộc loại i1 |
|
(I4) | comparator |
hàm | (C5) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C2), (C3) |
Giới hạn
- (C1)
0 < size(inputs)
. - (C2)
type(inputs...) = type(results...)
. - (C3)
same(shape(inputs...) + shape(results...))
. - (C4)
-R <= dimension < R
, trong đóR = rank(inputs[0])
. - (C5)
comparator
có loại(tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>
, trong đóEi = element_type(inputs[i])
.
Ví dụ
// %input0 = [[1, 2, 3], [3, 2, 1]]
// %input1 = [[3, 2, 1], [1, 2, 3]]
%result0, %result1 = "stablehlo.sort"(%input0, %input1) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>, %arg2: tensor<i64>, %arg3: tensor<i64>):
%predicate = "stablehlo.compare"(%arg0, %arg1) {
comparison_direction = #stablehlo<comparison_direction GT>
} : (tensor<i64>, tensor<i64>) -> tensor<i1>
"stablehlo.return"(%predicate) : (tensor<i1>) -> ()
}) {
dimension = 0 : i64,
is_stable = true
} : (tensor<2x3xi64>, tensor<2x3xi64>) -> (tensor<2x3xi64>, tensor<2x3xi64>)
// %result0 = [[3, 2, 3], [1, 2, 1]]
// %result1 = [[1, 2, 1], [3, 2, 3]]
sqrt
Ngữ nghĩa
Thực hiện phép tính căn bậc hai theo phần tử trên tensor operand
và tạo ra một tensor result
. Tuỳ thuộc vào loại phần tử, hãy thực hiện các thao tác sau:
- Đối với số thực độ chính xác đơn:
squareRoot
từ IEEE-754. - Đối với số phức: căn bậc hai phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(sqrt, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %operand: [[0.0, 1.0], [4.0, 9.0]]
%result = "stablehlo.sqrt"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [2.0, 3.0]]
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:
subtraction
từ IEEE-754. - Đối với số phức: phép trừ số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(subtract, lhs, rhs, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | lhs |
tensor số nguyên, dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
(I2) | rhs |
tensor số nguyên, dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor của số nguyên, dấu phẩy động hoặc kiểu phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
Ví dụ
// %lhs: [[6, 8], [10, 12]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.subtract"(%lhs, %rhs) : (tensor<2x2xf32>, tensor<2x2xf32>) -> (tensor<2x2xf32>)
// %result: [[1, 2], [3, 4]]
tan
Ngữ nghĩa
Thực hiện phép toán tiếp tuyến của các phần tử trên tensor operand
và tạo ra tensor result
. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
tan
từ IEEE-754. - Đối với số phức: tang phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(tan, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.tan"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [
// [0.0, 1.63312e+16],
// [0.0, 5.44375e+15]
// ]
tanh
Ngữ nghĩa
Thực hiện phép toán hàm tang hyperbol theo phần tử trên tensor operand
và tạo ra một tensor result
. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
tanh
từ IEEE-754. - Đối với số phức: tan hyperbol phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(tanh, operand, type(result))
.
Thông tin đầu vào
Hãng nhạc | Tên | Loại | Giới hạn |
---|---|---|---|
(I1) | operand |
tensor của dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá mỗi tensor | (C1) |
Kết quả đầu ra
Tên | Loại | Giới hạn |
---|---|---|
result |
tensor dấu phẩy động hoặc loại phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result)
.
Ví dụ
// %operand: [-1.0, 0.0, 1.0]
%result = "stablehlo.tanh"(%operand) : (tensor<3xf32>) -> tensor<3xf32>
// %result: [-0.76159416, 0.0, 0.76159416]
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)
permutation
là 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ụnga
như 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)
result
có 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)
cond
có loại(T0, ..., TN-1) -> tensor<i1>
, trong đóTi = type(operand[i])
. - (C2)
body
có loại(T0, ..., TN-1) -> (T0, ..., TN-1)
, trong đóTi = type(operand[i])
. - (C3)
type(results...) = type(operand...)
.
Ví dụ
// %init_i: 1
// %init_sum: 0
// %one: 1
// %ten: 10
%results0, %results1 = "stablehlo.while"(%init_i, %init_sum) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%cond = "stablehlo.compare"(%arg0, %ten) {
comparison_direction = #stablehlo<comparison_direction LT>
} : (tensor<i64>, tensor<i64>) -> tensor<i1>
stablehlo.return %cond : tensor<i1>
}, {
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%new_sum = stablehlo.add %arg1, %one : tensor<i64>
%new_i = stablehlo.add %arg0, %one : tensor<i64>
stablehlo.return %new_i, %new_sum : tensor<i64>, tensor<i64>
}) : (tensor<i64>, tensor<i64>) -> (tensor<i64>, tensor<i64>)
// %results0: 10
// %results1: 10
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
,complex
so 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ầnTensorElementType
hoặcQuantizedTensorElementType
củaTensorType
hoặcQuantizedTensorType
tương ứng.
def element_type(x: Value | Placeholder | Type):
if type(x) == TensorType:
return tensor_element_type(x)
if type(x) == QuantizedTensorType:
return quantized_tensor_element_type(x)
if type(x) is not Type:
return element_type(type(x))
is_per_axis_quantized(x: Value | Placeholder | Type) -> Value
là lối tắt chois_quantized(x) and quantization_dimension(x) is not None
.is_per_tensor_quantized(x: Value | Placeholder | Type) -> Value
là một lối tắt chois_quantized(x) and quantization_dimension(x) is None
.is_promotable(x: Type, y: Type) -> bool
kiểm tra xem có thể nâng cấp loạix
lên loạiy
hay không. Khix
vày
là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) -> Value
là 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ềtrue
nếux
làFloatType
. Nếux
là 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) -> Value
trả về giá trị lớn nhất của mộtTensorElementType
. Nếux
không phải làTensorElementType
, hãy trả vềNone
.min_value(x: Type) -> Value
trả về giá trị tối thiểu có thể của mộtTensorElementType
. Nếux
không phải làTensorElementType
, hãy trả vềNone
.member_name(x: Value | Placeholder | Type) -> Any
. Có sẵn cho tất cả các định nghĩa thành viênmember_name
thuộc mọi loại. Ví dụ:tensor_element_type(x)
trả về phầnTensorElementType
củaTensorType
tương ứng. Nếux
là 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ếux
không phải là loại có thành phần thích hợp, hoặc giá trị/phần giữ chỗ thuộc loại như vậy, hàm sẽ trả vềNone
.is_empty_algorithm(*args: Type)
kiểm tra xem tất cả các trường thuật toán dấu chấm có được đặt thànhNone
hay không. Điều này là cần thiết vì các thuật toán dấu chấm có hành vi mặc định được xác định trong quá trình triển khai, vì vậy, việc chỉ định giá trị mặc định sẽ không chính xác.
Xây dựng giá trị
operation_name(*xs: Value | Type) -> Value
. Hỗ trợ mọi hoạt động. Ví dụ:add(lhs, rhs)
nhận hai giá trị tensorlhs
vàrhs
rồi trả về kết quả đánh giá toán tửadd
với các giá trị đầu vào này. Đối với một số toán tử (ví dụ:broadcast_in_dim
), loại dữ liệu đầu ra của các toán tử đó là "tải trọng", tức là cần thiết để đánh giá một toán tử. Trong trường hợp này, hàm sẽ lấy các loại này làm đối số.
Hàm trên giá trị
Tất cả các toán tử và hàm của Python đều có sẵn. Ví dụ: cả ký hiệu subscription (đăng ký) và slicing (cắt) từ Python đều có thể được dùng để lập chỉ mục vào tensor, tensor lượng tử hoá và các bộ dữ liệu.
to_destination_type(x: Value, destination_type: Type) -> Value
được xác định trên các tensor và trả về giá trị đã chuyển đổi củax
dựa trêntype(x)
vàdestination_type
như sau:
def to_destination_type(x: Value, destination_type: Type) -> Value:
if type(x) == destination_type:
return x
if is_quantized(destination_type):
if is_quantized(type(x)):
return quantize(x, destination_type)
assert is_float(type(x))
return quantize(x, destination_type)
if is_quantized(type(x)):
assert destination_type = expressed_type(type(x))
return dequantize(type(x))
return convert(x, destination_type)
Có một cuộc thảo luận ban đầu về việc hợp nhất các thao tác convert
, uniform_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ềtrue
nếu tất cả các phần tử củax
làNaN
hoặcfalse
. Nếux
không phải là tensor, thì sẽ trả vềNone
.is_sorted(x: Value) -> Value
được xác định trên tensor và trả vềtrue
nếu các phần tử củ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ếux
không phải là một tensor, hãy trả vềNone
.is_unique(x: Value) -> Value
được xác định trên các tensor và trả vềtrue
nếux
không có phần tử trùng lặp hoặcfalse
nếu không. Nếux
không phải là tensor, thì sẽ trả vềNone
.member_name(x: Value) -> Any
được xác định cho tất cả các định nghĩa thành phầnmember_name
của tất cả các giá trị. Ví dụ:real_part(x)
trả về phầnRealPart
của mộtComplexConstant
tương ứng. Nếux
không phải là một giá trị có thành phần thích hợp, hãy trả vềNone
.same(x: Value) -> Value
được xác định trên các tensor và trả vềtrue
nếu tất cả các phần tử củax
đều bằng nhau hoặcfalse
nếu không. Nếu tensor không có phần tử, thì tensor đó được tính là "tất cả đều bằng nhau", tức là hàm sẽ trả vềtrue
. Nếux
không phải là tensor, hãy trả vềNone
.split(x: Value, num_results: Value, axis: Value) -> Value
được xác định trên các tensor và trả về các lát cắtnum_results
củax
dọc theo trụcaxis
. Nếux
khô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ềtrue
nếux
là tên của một hàm được xác định trong cùng phạm vi với hàm mẹ của toán tử có liên quan.is_namespaced_op_name(x: Value) -> Value
được xác định trên các chuỗi và trả vềtrue
nếux
là tên toán tử hợp lệ, tức là tuân theo biểu thức chính quy sau:[a-zA-Z][a-zA-Z0-9_]*([.][a-zA-Z0-9_$]+)+
Tính toán hình dạng
axes(x: Value | Placeholder | Type) -> Value
là lối tắt chorange(rank(x))
.dim(x: Value | Placeholder | Type, axis: Value) -> Value
là lối tắt choshape(x)[axis]
.dims(x: Value | Placeholder | Type, axes: List) -> List
là 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)
choTensorType
tương ứng được sắp xếp theo thứ tự từ điển tăng dần, tức là[0, ..., 0]
,[0, ..., 1]
, ...,shape(x) - 1
. Nếux
không phải là loại tensor, loại tensor lượng tử hoá hoặc giá trị hoặc phần giữ chỗ của một trong các loại này, hãy trả vềNone
.rank(x: Value | Placeholder | Type) -> Value
là lối tắt 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) -> Value
là 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) -> Type
là 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_max
vàquantization_dimension
(đối với loại lượng tử hoá trên mỗi trục) phải khớp nhau, nhưngscales
vàzero points
có thể khác nhau.
def baseline_type(x: Value | Placeholder | Type) -> Type:
if type(x) == TensorType:
return x
if type(x) == QuantizedTensorType:
element_type = quantized_tensor_element_type(x)
baseline_element_type = QuantizedTensorElementType(
storage_type = storage_type(element_type),
storage_min = storage_min(element_type),
storage_max = storage_max(element_type),
expressed_type = expressed_type(element_type),
quantization_dimension = quantization_dimension(element_type),
scales = [constant(1.0, expressed_type(element_type))] * dim(x, quantization_dimension(element_type)),
zero_points = [constant(0, storage_type(element_type))] * dim(x, quantization_dimension(element_type)))
return QuantizedTensorType(shape(x), baseline_element_type)
if type(x) is not Type:
return baseline_element_type(type(x))
dequantize
được xác định trên các loại tensor lượng tử hoá và biến đổi các loại tensor đó thành các loại tensor dấu phẩy động. Điều này xảy ra thông qua việc chuyển đổi các phần tử được lượng tử hoá đại diện cho các giá trị số nguyên của loại bộ nhớ thành các giá trị dấu phẩy động tương ứng của loại được biểu thị bằng cách sử dụng điểm không và tỷ lệ liên kết với loại phần tử được lượng tử hoá.
def compute_zero_points(quantized_type, result_type):
if is_per_tensor_quantized(quantized_type):
return broadcast_in_dim(constant(zero_point(quantized_type), storage_type(quantized_type)), [], result_type)
if is_per_axis_quantized(quantized_type):
for i in index_space(result_type):
d = quantization_dimension(quantized_type)
zero_points[i] = zero_points(quantized_type)[i[d]]
return zero_points
def compute_scales(quantized_type, result_type):
if is_per_tensor_quantized(quantized_type):
return broadcast_in_dim(constant(scale(quantized_type), expressed_type(quantized_type)), [],
type(result_type))
if is_per_axis_quantized(quantized_type):
for i in index_space(result_type):
d = quantization_dimension(quantized_type)
scales[i] = scales(quantized_type)[i[d]]
return scales
def dequantize(x: Value) -> Value:
assert is_quantized(x)
x_storage = bitcast_convert(x, storage_type(x))
x_storage_sub = x_storage - compute_zero_points(type(x), type(x_storage))
x_expressed_sub = convert(x_storage_sub, expressed_type(x))
return x_expressed_sub * compute_scales(type(x), type(x_expressed_sub))
quantize
được xác định trên các loại tensor dấu phẩy động và biến đổi các loại tensor đó thành các loại tensor lượng tử hoá. Điều này xảy ra thông qua việc chuyển đổi các giá trị dấu phẩy động của loại được biểu thị thành các giá trị số nguyên tương ứng của loại bộ nhớ bằng cách sử dụng điểm không và tỷ lệ được liên kết với loại phần tử được lượng tử hoá.
def quantize(x: Value, result_type: Type) -> Value:
assert is_float(x) and is_quantized(result_type)
zero_points = compute_zero_points(result_type, TensorType(shape(x), storage_type(result_type)))
converted_zero_points = convert(zero_points, expressed_type(result_type))
converted_min = convert(storage_min(result_type), expressed_type(result_type))
converted_max = convert(storage_max(result_type), expressed_type(result_type))
x_scaled = x / compute_scales(result_type, type(x))
x_scaled_add_zp = x_scaled + converted_zero_points
x_clamped = clamp(converted_min, x_scaled_add_zp, converted_max)
x_rounded = round_nearest_even(x_clamped)
return convert(x_rounded, result_type)
dequantize_op_quantize
được dùng để chỉ định các phép tính theo phần tử trên các tensor được lượng tử hoá. Phương thức này giải lượng tử hoá, tức là chuyển các phần tử lượng tử hoá thành loại được biểu thị, sau đó thực hiện một phép toán, rồi lượng tử hoá, tức là chuyển kết quả trở lại loại bộ nhớ. Hiện tại, hàm này chỉ hoạt động cho việc lượng tử hoá theo từng tensor. Đang tiến hành định lượng trên mỗi trục (#1574).
def dequantize_op_quantize(op, *inputs_and_output_type):
inputs = inputs_and_output_type[:-1]
output_type = inputs_and_output_type[-1]
float_inputs = map(dequantize, inputs)
float_result = op(*float_inputs)
return quantize(float_result, output_type)
def dequantize_batch_norm_grad_or_training_quantize(op, *inputs_and_output_types):
inputs = inputs_and_output_type[:-3]
float_inputs = map(dequantize, inputs)
float_results = op(*float_inputs)
return map(quantize, float_results, inputs_and_output_type[-3:])
def dequantize_compare(lhs, rhs, comparison_direction):
float_lhs = dequantize(lhs)
float_rhs = dequantize(rhs)
return compare(float_lhs, float_rhs, comparison_direction, FLOAT)
def dequantize_select_quantize(pred, on_true, on_false, output_type):
float_on_true = dequantize(on_true)
float_on_false = dequantize(on_false)
float_result = select(pred, float_on_true, float_on_false)
return quantize(float_result, output_type)
hybrid_dequantize_then_op
dùng để chỉ định lượng tử hoá chỉ trọng số cho op kết hợp chấp nhận lh trong dấu phẩy động và rhs trong các kiểu lượng tử hoá. Hàm này giải lượng tử hoá các đầu vào được lượng tử hoá thành các loại được biểu thị và thực hiện phép tính bằng số thực. Loại phần tử của tensor lhs float và loại biểu thị của tensor rhs quantized phải giống hệt nhau.
def hybrid_dequantize_then_op(op, lhs, rhs):
assert(is_float(lhs) and is_quantized(rhs) and element_type(lhs) == expressed_type(rhs))
return op(lhs, dequantize(rhs))
Tính toán lưới
cross_partition(replica_groups: Value) -> Value
. Xem phần "cross_replica" ở trên.cross_replica(replica_groups: Value) -> Value
. Xem mục "cross_replica" ở trên.cross_replica_and_partition(replica_groups: Value) -> Value
. Xem phần "cross_replica_and_partition" ở trên.flattened_ids(replica_groups: Value) -> Value
. Xem phần "flattened_ids" ở trên.
Tính năng động
Giá trị StableHLO có thể có kích thước phương diện động, ví dụ: tensor<?xi64>
.
Tuy nhiên, các giá trị StableHLO không thể có số lượng phương diện động (động lực không được xếp hạng, ví dụ: tensor<*xi64>
). Toán hạng và kết quả được phép sử dụng kích thước phương diện động, ngay cả khi có quy tắc ràng buộc về kích thước. Các quy tắc ràng buộc sẽ được xác minh tĩnh nếu có thể, nếu không thì các quy tắc đó sẽ bị trì hoãn trong thời gian chạy và sự không khớp sẽ dẫn đến hành vi không xác định. Hãy xem ví dụ bên dưới.
Hình dạng không khớp đối với các toán tử đơn phần tử
Hãy xem xét chương trình đồ chơi sau:
func.func @foo(%arg0: tensor<?xf64>) {
%0 = stablehlo.abs %arg0 : (tensor<?xf64>) -> tensor<2xf64>
return
}
Một chương trình như vậy là không bình thường, vì thông thường, bạn sẽ biết hình dạng của kết quả nhưng không biết hình dạng của dữ liệu đầu vào. Tuy nhiên, đây là một chương trình StableHLO hợp lệ. Không thể xác thực tĩnh toán tử abs
trong chương trình này vì không xác định được hình dạng chính xác của toán hạng. Tuy nhiên, các hình dạng này chắc chắn tương thích và bạn có thể kiểm tra tĩnh: ?
có thể trở thành 2
trong thời gian chạy và sẽ không có vấn đề gì. Tuy nhiên, ?
cũng có thể trở thành một số nguyên khác, trong trường hợp đó thì hành vi không xác định.
Lưu ý rằng nếu kích thước thứ nguyên có tính động trong kết quả, thì không thể có hành vi không xác định. Trên thực tế, không có kích thước "dự kiến" do đó không thể có trường hợp không khớp.
Hình dạng không khớp cho các phép toán theo phần tử nhị phân
Hãy xem xét chương trình đồ chơi sau:
func.func @foo(%arg0: tensor<?xf64>, %arg1: tensor<?xf64>) {
%0 = stablehlo.add %arg0, %arg0 : (tensor<?xf64>, tensor<?xf64>) -> tensor<?xf64>
return
}
Khi nói đến các phép toán nhị phân theo phần tử, hình dạng của dữ liệu đầu vào và kết quả phải khớp nhau trong thời gian chạy. Tại thời điểm biên dịch, các phương diện tĩnh phải bằng nhau, nếu không, các phương diện này chỉ cần tương thích. Nếu bất kỳ phương diện nào là phương diện động trong đầu vào, thì có thể có hành vi không xác định trong thời gian chạy, vì kích thước động có thể không khớp với kích thước tương ứng trong toán hạng khác (là tĩnh hoặc động). Nếu tất cả dữ liệu đầu vào đều tĩnh, thì kết quả có động hay không không quan trọng: các phương diện đã biết tĩnh sẽ được kiểm tra tĩnh và các phương diện động không áp đặt bất kỳ quy tắc ràng buộc nào.
Hình dạng không khớp đối với hoạt động nhận hình dạng đầu ra làm toán hạng
Hãy xem xét chương trình đồ chơi sau:
func.func @foo(%arg0: tensor<2xi32>) {
%0 = stablehlo.dynamic_iota %arg0, dim = 0 : (tensor<2xi32>) -> tensor<3x4xi64>
return
}
Các giá trị trong toán hạng hình dạng trong thời gian chạy phải khớp với hình dạng của kết quả, nếu không hành vi sẽ không xác định. Tức là trong thời gian chạy, %arg0
phải có giá trị là dense<[3, 4]> : tensor<2xi32>
. Nếu toán hạng hình dạng là hằng số, thì bạn có thể xác minh điều này theo cách tĩnh. Nếu hình dạng kết quả hoàn toàn linh động, thì không thể có sự không khớp.