StableHLO là một tập hợp các thao tác cho các thao tác cấp cao (HLO) trong các mô hình học máy (ML). StableHLO hoạt động như một lớp di động giữa các trình biên dịch ML và khung ML khác nhau: Các khung ML tạo ra các chương trình StableHLO tương thích với các trình biên dịch ML sử dụng các chương trình StableHLO.
Mục tiêu của chúng tôi là đơn giản hoá và đẩy nhanh quá trình phát triển ML bằng cách tạo khả năng tương tác cao hơn giữa nhiều khung ML (chẳng hạn như TensorFlow, JAX và PyTorch) và trình biên dịch ML (chẳng hạn như XLA và IREE). Để đạt được mục tiêu đó, tài liệu này cung cấp một quy cách cho ngôn ngữ lập trình StableHLO.
Quy cách này có 3 phần chính. Trước tiên, phần Chương trình mô tả cấu trúc của các chương trình StableHLO bao gồm các hàm StableHLO, bản thân các hàm này bao gồm các thao tác StableHLO. Trong cấu trúc đó, phần Ops chỉ định ngữ nghĩa của từng thao tác. Phần Thực thi cung cấp ngữ nghĩa cho tất cả các thao tác này cùng thực thi trong một chương trình. Cuối cùng, phần Ký hiệu sẽ thảo luận về ký hiệu được dùng trong toàn bộ quy cách.
Để xem quy cách của một bản phát hành trước của StableHLO, hãy mở kho lưu trữ tại bản phát hành được gắn thẻ mà bạn quan tâm. Ví dụ: Quy cách StableHLO phiên bản 0.19.0. Để xem những thay đổi xảy ra ở mỗi lần tăng phiên bản phụ của StableHLO, hãy tham khảo nhật ký phiên bản trong VhloDialect.td.
Chương trình
Program ::= {Func}
Các chương trình StableHLO bao gồm một số lượng tuỳ ý các hàm StableHLO.
Dưới đây là một chương trình mẫu có hàm @main với 3 đầu vào (%image, %weights và %bias) và 1 đầu ra. Nội dung của hàm có 6 thao tác.
func.func @main(
%image: tensor<28x28xf32>,
%weights: tensor<784x10xf32>,
%bias: tensor<1x10xf32>
) -> tensor<1x10xf32> {
%0 = "stablehlo.reshape"(%image) : (tensor<28x28xf32>) -> tensor<1x784xf32>
%1 = "stablehlo.dot"(%0, %weights) : (tensor<1x784xf32>, tensor<784x10xf32>) -> tensor<1x10xf32>
%2 = "stablehlo.add"(%1, %bias) : (tensor<1x10xf32>, tensor<1x10xf32>) -> tensor<1x10xf32>
%3 = "stablehlo.constant"() {value = dense<0.0> : tensor<1x10xf32>} : () -> tensor<1x10xf32>
%4 = "stablehlo.maximum"(%2, %3) : (tensor<1x10xf32>, tensor<1x10xf32>) -> tensor<1x10xf32>
"func.return"(%4): (tensor<1x10xf32>) -> ()
}
Hàm
Func ::= 'func' '.' 'func' FuncId FuncInputs FuncOutputs '{' FuncBody '}'
FuncInputs ::= '(' [FuncInput {',' FuncInput}] `)`
FuncInput ::= ValueId ':' ValueType
FuncOutputs ::= ['->' FuncOutput, {',' FuncOutput}]
FuncOutput ::= ValueType
FuncBody ::= {Op}
Các hàm StableHLO (còn được gọi là các hàm được đặt tên) có một giá trị nhận dạng, dữ liệu đầu vào/đầu ra và một phần nội dung. Trong tương lai, chúng tôi dự định giới thiệu thêm siêu dữ liệu cho các hàm để đạt được khả năng tương thích tốt hơn với HLO (#425, #626, #740, #744).
Giá trị nhận dạng
FuncId ::= '@' letter {letter | digit}
ValueId ::= '%' digit {digit}
| '%' letter {letter | digit}
letter ::= 'a' | ... | 'z' | 'A' | ... | 'Z' | '_'
digit ::= '0' | ... | '9'
Giá trị nhận dạng StableHLO tương tự như giá trị nhận dạng trong nhiều ngôn ngữ lập trình, với 2 đặc điểm riêng biệt: 1) tất cả giá trị nhận dạng đều có ký hiệu để phân biệt các loại giá trị nhận dạng, 2) giá trị nhận dạng có thể hoàn toàn là số để đơn giản hoá việc tạo chương trình StableHLO.
Loại
Type ::= ValueType | NonValueType
ValueType ::= TensorType | QuantizedTensorType | TokenType | TupleType | BufferType
NonValueType ::= TensorElementType | QuantizedTensorElementType | FunctionType | StringType
Các loại StableHLO được phân loại thành các loại giá trị (còn được gọi là các loại hạng nhất) đại diện cho các giá trị StableHLO và các loại không phải giá trị mô tả các phần tử khác của chương trình. Các loại StableHLO tương tự như các loại trong nhiều ngôn ngữ lập trình, điểm đặc biệt chính là bản chất dành riêng cho miền của StableHLO, dẫn đến một số kết quả bất thường (ví dụ: các loại vô hướng không phải là các loại giá trị).
TensorType ::= 'tensor' '<' Shape TensorElementType '>'
Shape ::= {DimensionSize 'x'}
DimensionSize ::= digit {digit} | '?'
Các loại tensor biểu thị tensor, tức là mảng đa chiều. Chúng có một hình dạng và một loại phần tử, trong đó hình dạng biểu thị kích thước chiều không âm hoặc không xác định theo thứ tự tăng dần của các chiều tương ứng (còn được gọi là trục) được đánh số từ 0 đến R-1. Số lượng phương diện R được gọi là hạng. Ví dụ: tensor<2x3xf32> là một loại tensor có hình dạng 2x3 và loại phần tử f32. Mảng này có 2 phương diện (hay nói cách khác là 2 trục) – phương diện thứ 0 và phương diện thứ 1 – có kích thước là 2 và 3. Thứ hạng của nó là 2.
Các hình dạng có thể không xác định được một phần hoặc hoàn toàn (động), ví dụ: tensor<?x2xf64> không xác định được một phần và tensor<?x?xf64> không xác định được hoàn toàn. Kích thước phương diện động được biểu thị bằng ?. Không thể huỷ xếp hạng hình dạng.
Trong tương lai, chúng tôi dự định khám phá việc mở rộng các loại tensor ngoài kích thước và loại phần tử, ví dụ: bao gồm bố cục (#629) và độ thưa thớt (#1078).
QuantizedTensorType ::= 'tensor' '<' Shape QuantizedTensorElementType '>'
QuantizedTensorElementType ::= '!quant.uniform' '<'
QuantizationStorageType
['<' QuantizationStorageMin ':' QuantizationStorageMax '>']
':' QuantizationExpressedType
[':' QuantizationDimension]
',' QuantizationParameters '>'
QuantizationStorageType ::= IntegerType
QuantizationStorageMin ::= IntegerLiteral
QuantizationStorageMax ::= IntegerLiteral
QuantizationExpressedType ::= FloatType
QuantizationDimension ::= IntegerLiteral
QuantizationParameters ::= QuantizationParameter
| '{' QuantizationParameter {',' QuantizationParameter} '}'
QuantizationParameter ::= QuantizationScale [':' QuantizationZeroPoint]
QuantizationScale ::= FloatLiteral
QuantizationZeroPoint ::= IntegerLiteral
| Tên | Loại | Giới hạn |
|---|---|---|
storage_type |
loại số nguyên | (C1-C3), (C8) |
storage_min |
hằng số nguyên | (C1), (C3), (C7) |
storage_max |
hằng số nguyên | (C2), (C3), (C7) |
expressed_type |
loại dấu phẩy động | (C4) |
quantization_dimension |
hằng số nguyên không bắt buộc | (C10-C12) |
scales |
số lượng hằng số có thể thay đổi của số thực | (C4-C6), (C9), (C10), (C13) |
zero_points |
số lượng hằng số nguyên có thể thay đổi | (C7 – C9) |
Các loại phần tử được lượng tử hoá biểu thị các giá trị nguyên của một loại lưu trữ trong phạm vi từ storage_min đến storage_max (bao gồm) tương ứng với các giá trị dấu phẩy động của một loại được biểu thị. Đối với một giá trị số nguyên i nhất định, giá trị dấu phẩy động f tương ứng có thể được tính là f = (i - zero_point) * scale, trong đó scale và zero_point được gọi là các tham số định lượng. storage_min và storage_max là các giá trị không bắt buộc trong ngữ pháp, nhưng có các giá trị mặc định là min_value(storage_type) và max_value(storage_type) tương ứng. Các loại phần tử được lượng tử hoá có những ràng buộc sau:
- (C1)
type(storage_min) = storage_type. - (C2)
type(storage_max) = storage_type. - (C3)
min_value(storage_type) <= storage_min < storage_max <= max_value(storage_type). - (C4)
type(scales...) = expressed_type. - (C5)
0 < scales. - (C6)
is_finite(scales...). - (C7)
storage_min <= zero_points <= storage_max. - (C8)
type(zero_points...) = storage_type. - (C9)
size(scales) = size(zero_points). - (C10) Nếu
is_empty(quantization_dimension), thìsize(scales) = 1. - (C11)
0 <= quantization_dimension.
Hiện tại, QuantizationScale là một hằng số dấu phẩy động, nhưng có nhiều người quan tâm đến các tỷ lệ dựa trên số nguyên, được biểu thị bằng hệ số nhân và các giá trị dịch chuyển. Chúng tôi dự định sẽ khám phá vấn đề này trong tương lai gần (#1404).
Hiện đang có một cuộc thảo luận về ngữ nghĩa của QuantizationZeroPoint, bao gồm loại, giá trị và liệu có thể chỉ có một hoặc có thể có nhiều điểm 0 trong một loại tensor được lượng tử hoá hay không. Dựa trên kết quả của cuộc thảo luận này, quy cách về điểm bằng 0 có thể thay đổi trong tương lai (#1405).
Một cuộc thảo luận khác đang diễn ra liên quan đến ngữ nghĩa của QuantizationStorageMin và QuantizationStorageMax để xác định xem có nên áp đặt bất kỳ hạn chế nào đối với các giá trị này và đối với các giá trị của tensor được lượng tử hoá hay không (#1406).
Cuối cùng, chúng tôi dự định khám phá cách biểu thị các tỷ lệ không xác định và điểm không, tương tự như cách chúng tôi dự định khám phá cách biểu thị các kích thước phương diện không xác định (#1407).
Các loại tensor lượng tử hoá biểu thị các tensor có phần tử lượng tử hoá. Các tensor này hoàn toàn giống với tensor thông thường, ngoại trừ việc các phần tử của chúng có các loại phần tử được lượng tử hoá, thay vì các loại phần tử thông thường.
Trong các tensor được lượng tử hoá, quá trình lượng tử hoá có thể là trên mỗi tensor, tức là có một scale và zero_point cho toàn bộ tensor hoặc có thể là trên mỗi trục, tức là có nhiều scales và zero_points, một cặp cho mỗi lát cắt của một phương diện cụ thể quantization_dimension. Cụ thể hơn, trong một tensor t có lượng tử hoá trên mỗi trục, sẽ có dim(t, quantization_dimension) lát của quantization_dimension: t[:, ..., 0, ..., :], t[:, ..., 1, ..., :], v.v. Tất cả các phần tử trong lát thứ i đều dùng scales[i] và zero_points[i] làm tham số lượng tử hoá. Các loại tensor được lượng tử hoá có những ràng buộc sau:
- Đối với lượng tử hoá trên mỗi tensor:
- Không có thêm ràng buộc nào khác.
- Đối với lượng tử hoá trên mỗi trục:
- (C12)
quantization_dimension < rank(self). - (C13)
dim(self, quantization_dimension) = size(scales).
- (C12)
TokenType ::= 'token'
Loại mã thông báo biểu thị mã thông báo, tức là các giá trị không công khai do một số thao tác tạo và sử dụng. Mã thông báo được dùng để áp đặt thứ tự thực thi cho các thao tác như mô tả trong phần Thực thi.
TupleType ::= 'tuple' '<' TupleElementTypes '>'
TupleElementTypes ::= [ValueType {',' ValueType}]
Các loại bộ đệm đại diện cho bộ đệm. Ví dụ: trong XLA, các vùng đệm là mảng đa chiều có bộ nhớ nhất quán. Tương tự như các loại tensor, các loại vùng đệm có một hình dạng và một loại phần tử, trong đó hình dạng biểu thị kích thước chiều không âm hoặc không xác định theo thứ tự tăng dần của các chiều tương ứng (còn được gọi là trục) được đánh số từ 0 đến R-1. Số lượng phương diện R được gọi là hạng. Ví dụ: memref<2x3xf32> là một loại vùng đệm có hình dạng 2x3 và loại phần tử f32. Nó có 2 phương diện (hay nói cách khác là 2 trục) – phương diện thứ 0 và phương diện thứ 1 – có kích thước là 2 và 3. Thứ hạng của nó là 2.
Bạn có thể phân bổ các vùng đệm bằng cách sử dụng custom_call thành CreateBuffer hoặc Pin và huỷ phân bổ thông qua custom_call thành Unpin. Chỉ các thao tác custom_call mới có thể đọc và ghi nội dung bên trong các vùng đệm. Hãy xem custom_call để biết thêm thông tin chi tiết.
Các loại bộ biểu thị các bộ, tức là các danh sách không đồng nhất. Bộ giá trị là một tính năng cũ chỉ tồn tại để tương thích với HLO. Trong HLO, các bộ được dùng để biểu thị đầu vào và đầu ra có thể thay đổi. Trong StableHLO, các đầu vào và đầu ra có số lượng thay đổi được hỗ trợ một cách tự nhiên và việc sử dụng bộ dữ liệu duy nhất trong StableHLO là để thể hiện toàn diện ABI HLO, trong đó chẳng hạn như T, tuple<T> và tuple<tuple<T>> có thể khác biệt đáng kể tuỳ thuộc vào một cách triển khai cụ thể. Trong tương lai, chúng tôi dự định thay đổi ABI HLO. Điều này có thể cho phép chúng tôi xoá các loại bộ dữ liệu khỏi StableHLO (#598).
TensorElementType ::= BooleanType | IntegerType | FloatType | ComplexType
BooleanType ::= 'i1'
IntegerType ::= SignedIntegerType | UnsignedIntegerType
SignedIntegerType ::= 'si2' | 'si4' | 'si8' | 'si16' | 'si32' | 'si64'
UnsignedIntegerType ::= 'ui2' | 'ui4' | 'ui8' | 'ui16' | 'ui32' | 'ui64'
FloatType ::= 'f4E2M1FN' | 'f6E2M3FN' | 'f6E3M2FN' | 'f8E3M4' | 'f8E4M3'
| 'f8E4M3FN' | 'f8E4M3FNUZ' | 'f8E4M3B11FNUZ' | 'f8E5M2'
| 'f8E5M2FNUZ' | 'f8E8M0FNU' | 'bf16' | 'f16' | 'f32' | 'f64'
TensorFloat32 ::= 'tf32'
ComplexType ::= 'complex' '<' ComplexElementType '>'
ComplexElementType ::= 'f32' | 'f64'
Các loại phần tử đại diện cho các phần tử của các loại tensor. Không giống như trong nhiều ngôn ngữ lập trình, các loại này không phải là loại hạng nhất trong StableHLO. Điều này có nghĩa là các chương trình StableHLO không thể trực tiếp biểu thị các giá trị thuộc những loại này (do đó, theo thông lệ, bạn nên biểu thị các giá trị vô hướng thuộc loại T bằng các giá trị tensor 0 chiều thuộc loại tensor<T>).
- Loại boolean biểu thị các giá trị boolean
truevàfalse. - Các loại số nguyên có thể là số nguyên có dấu (
si) hoặc số nguyên không dấu (ui) và có một trong các độ rộng bit được hỗ trợ (2,4,8,16,32hoặc64). Các loạisiNcó dấu biểu thị các giá trị số nguyên từ-2^(N-1)đến2^(N-1)-1(bao gồm cả 2 giá trị này), còn các loạiuiNkhông dấu biểu thị các giá trị số nguyên từ0đến2^N-1(bao gồm cả 2 giá trị này). - Các loại số thực có thể là một trong những loại sau:
f8E3M4,f8E4M3vàf8E5M2là các số dấu phẩy động 8 bit tuân theo quy ước IEEE-754.- Các loại
f8E4M3FNvàf8E5M2tương ứng với các phương thức mã hoáE4M3vàE5M2của định dạng FP8 được mô tả trong Định dạng FP8 cho học sâu. - Các loại
f8E4M3FNUZvàf8E5M2FNUZtương ứng với các mã hoáE4M3vàE5M2của các định dạng FP8 được mô tả trong Định dạng số 8 bit cho mạng nơ-ron sâu. f8E4M3B11FNUZtương ứng với phương thức mã hoáE4M3của các định dạng FP8 được mô tả trong Huấn luyện và suy luận dấu phẩy động 8 bit kết hợp (HFP8) cho mạng nơ-ron sâu.- Loại
bf16tương ứng với định dạngbfloat16được mô tả trong BFloat16: Bí quyết để đạt hiệu suất cao trên Cloud TPU. - Các loại
f16,f32vàf64tương ứng với các định dạngbinary16("độ bán chính xác"),binary32("độ chính xác đơn") vàbinary64("độ chính xác kép") được mô tả trong tiêu chuẩn IEEE 754. - Loại
tf32tương ứng với định dạng TensorFloat32 và có phạm vi hỗ trợ hạn chế trong StableHLO. f4E2M1FN,f6E2M3FN,f6E3M2FNvàf8E8M0FNUMX (thu nhỏ) được mô tả trong Quy cách định dạng thu nhỏ OCP.
- Các kiểu phức tạp biểu thị các giá trị phức tạp có phần thực và phần ảo của cùng một kiểu phần tử. Các loại phức tạp được hỗ trợ là
complex<f32>(cả hai phần đều thuộc loạif32) vàcomplex<f64>(cả hai phần đều thuộc loạif64).
FunctionType ::= '(' InputTypes ')' '->' '(' OutputTypes ')'
InputTypes ::= [ValueType {',' ValueType}]
OutputTypes ::= [ValueType {',' ValueType}]
Các loại hàm biểu thị cả hàm được đặt tên và hàm ẩn danh. Chúng có các loại đầu vào (danh sách các loại ở phía bên trái của ->) và các loại đầu ra (danh sách các loại ở phía bên phải của ->). Trong nhiều ngôn ngữ lập trình, các loại hàm là loại đầu tiên, nhưng không phải trong StableHLO.
StringType ::= 'string'
Loại chuỗi biểu thị các chuỗi byte. Không giống như trong nhiều ngôn ngữ lập trình, loại chuỗi không phải là loại dữ liệu hạng nhất trong StableHLO và chỉ được dùng để chỉ định siêu dữ liệu tĩnh cho các phần tử chương trình.
Hoạt động tính toán
Các thao tác StableHLO (còn được gọi là ops) đại diện cho một tập hợp khép kín gồm các thao tác cấp cao trong các mô hình học máy. Như đã thảo luận ở trên, cú pháp StableHLO chịu ảnh hưởng lớn của MLIR. Đây không nhất thiết là lựa chọn thay thế tiện dụng nhất, nhưng có thể nói là phù hợp nhất với mục tiêu của StableHLO là tạo ra khả năng tương tác cao hơn giữa các khung ML và trình biên dịch ML.
Op ::= [OpOutputs] OpName OpInputs ':' OpSignature
OpName ::= '"' 'stablehlo' '.' OpMnemonic '"'
OpMnemonic ::= 'abs' | 'add' | ...
Các thao tác StableHLO (còn được gọi là ops) có tên, dữ liệu đầu vào/đầu ra và chữ ký. Tên này bao gồm tiền tố stablehlo. và một từ viết tắt giúp xác định duy nhất một trong các thao tác được hỗ trợ. Xem bên dưới để biết danh sách đầy đủ tất cả các thao tác được hỗ trợ.
OpInputs ::= OpInputValues OpInputFuncs OpInputAttrs
OpInputValues ::= '(' [OpInputValue {',' OpInputValue}] ')'
OpInputValue ::= ValueId
OpInputFuncs ::= ['(' OpInputFunc {',' OpInputFunc} ')']
OpInputAttrs ::= ['{' OpInputAttr {',' OpInputAttr} '}']
OpOutputs ::= [OpOutput {',' OpOutput} '=']
OpOutput ::= ValueId
Các thao tác sử dụng đầu vào và tạo ra đầu ra. Đầu vào được phân loại thành các giá trị đầu vào (được tính toán trong quá trình thực thi), hàm đầu vào (được cung cấp tĩnh, vì trong StableHLO, các hàm không phải là giá trị lớp thứ nhất) và các thuộc tính đầu vào (cũng được cung cấp tĩnh). Loại đầu vào và đầu ra mà một thao tác sử dụng và tạo ra phụ thuộc vào từ viết tắt của thao tác đó. Ví dụ: thao tác add sử dụng 2 giá trị đầu vào và tạo ra 1 giá trị đầu ra. Để so sánh, thao tác select_and_scatter sẽ sử dụng 3 giá trị đầu vào, 2 hàm đầu vào và 3 thuộc tính đầu vào.
OpInputFunc ::= '{' Unused FuncInputs ':' FuncBody '}'
Unused ::= '^' digit {digit}
| '^' letter {letter | digit}
Hàm đầu vào (còn được gọi là hàm ẩn danh) rất giống với hàm được đặt tên, ngoại trừ: 1) chúng không có giá trị nhận dạng (do đó có tên là "ẩn danh"), 2) chúng không khai báo các loại đầu ra (các loại đầu ra được suy luận từ thao tác return trong hàm).
Cú pháp cho các hàm đầu vào bao gồm một phần hiện không dùng đến (xem phần Unused ở trên) để tương thích với MLIR. Trong MLIR, có một khái niệm chung hơn về "vùng" có thể có nhiều "khối" của các thao tác được kết nối với nhau thông qua các thao tác nhảy. Các khối này có mã nhận dạng tương ứng với Unused sản phẩm, nhờ đó bạn có thể phân biệt các khối với nhau.
StableHLO không có các thao tác nhảy, nên phần tương ứng của cú pháp MLIR không được dùng (nhưng vẫn có ở đó).
OpInputAttr ::= OpInputAttrName '=' OpInputAttrValue
OpInputAttrName ::= letter {letter | digit}
OpInputAttrValue ::= Constant
Thuộc tính đầu vào có tên và giá trị là một trong các hằng số được hỗ trợ. Đây là cách chính để chỉ định siêu dữ liệu tĩnh cho các phần tử chương trình. Ví dụ: thao tác concatenate sử dụng thuộc tính dimension để chỉ định phương diện mà theo đó các giá trị đầu vào được nối. Tương tự, slice op sử dụng nhiều thuộc tính như start_indices và limit_indices để chỉ định các ranh giới được dùng để cắt giá trị đầu vào.
Hiện tại, các chương trình StableHLO trong tự nhiên đôi khi chứa các thuộc tính không được mô tả trong tài liệu này. Trong tương lai, chúng tôi dự định sẽ đưa những thuộc tính này vào opset StableHLO hoặc cấm chúng xuất hiện trong các chương trình StableHLO. Trong thời gian chờ đợi, bạn có thể xem danh sách các thuộc tính này tại đây:
layout(#629).mhlo.frontend_attributes(#628).mhlo.sharding(#619).output_operand_aliases(#740).- Siêu dữ liệu vị trí (#594).
OpSignature ::= '(' [ValueType {',' ValueType}] ')' '->' '(' [ValueType {',' ValueType}] ')'
Chữ ký Op bao gồm các loại của tất cả giá trị đầu vào (danh sách các loại ở phía bên trái của ->) và các loại của tất cả giá trị đầu ra (danh sách các loại ở phía bên phải của ->). Nói một cách chính xác, các loại đầu vào là dư thừa và các loại đầu ra cũng gần như luôn dư thừa (vì đối với hầu hết các hoạt động StableHLO, các loại đầu ra có thể được suy luận từ đầu vào). Tuy nhiên, op
signature là một phần trong cú pháp StableHLO để tương thích với MLIR.
Sau đây là một ví dụ về thao tác có mã nhớ là select_and_scatter. Thao tác này sử dụng 3 giá trị đầu vào (%operand, %source 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 thao tác chỉ bao gồm các loại giá trị đầu vào (nhưng không bao gồm các loại hàm và thuộc tính đầu vào được cung cấp nội tuyến).
%result = "stablehlo.select_and_scatter"(%operand, %source, %init_value) ({
^bb0(%arg0: tensor<i32>, %arg1: tensor<i32>):
%0 = "stablehlo.compare"(%arg0, %arg1) {
comparison_direction = #stablehlo<comparison_direction GE>
} : (tensor<i32>, tensor<i32>) -> tensor<i1>
"stablehlo.return"(%0) : (tensor<i1>) -> ()
}, {
^bb0(%arg0: tensor<i32>, %arg1: tensor<i32>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
"stablehlo.return"(%0) : (tensor<i32>) -> ()
}) {
window_dimensions = dense<[3, 1]> : tensor<2xi64>,
window_strides = dense<[2, 1]> : tensor<2xi64>,
padding = dense<[[0, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<4x2xi32>, tensor<2x2xi32>, tensor<i32>) -> tensor<4x2xi32>
Hằng số
Constant ::= BooleanConstant
| IntegerConstant
| FloatConstant
| ComplexConstant
| TensorConstant
| QuantizedTensorConstant
| StringConstant
| EnumConstant
Hằng số StableHLO có một chữ và một loại, cùng nhau biểu thị một giá trị StableHLO. Nhìn chung, kiểu này là một phần của cú pháp hằng số, ngoại trừ khi kiểu này không rõ ràng (ví dụ: hằng số boolean có kiểu i1 không rõ ràng, trong khi hằng số nguyên có thể có nhiều kiểu có thể có).
BooleanConstant ::= BooleanLiteral
BooleanLiteral ::= 'true' | 'false'
Hằng số boolean biểu thị các giá trị boolean 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ố nguyên biểu thị các giá trị nguyên thông qua các chuỗi sử dụng ký hiệu thập phân hoặc thập lục phân. Các cơ số khác, chẳng hạn như cơ số nhị phân hoặc cơ số bát phân, không được hỗ trợ. Hằng số nguyên có các ràng buộc sau:
- (C1)
is_wellformed(integer_literal, integer_type).
FloatConstant ::= FloatLiteral ':' FloatType
FloatLiteral ::= SignPart IntegerPart FractionalPart ScientificPart
| '0x' [HexadecimalDigits]
SignPart ::= ['-' | '+']
IntegerPart ::= DecimalDigits
FractionalPart ::= ['.' [DecimalDigits]]
ScientificPart ::= [('e' | 'E') ['-' | '+'] DecimalDigits]
Hằng số dấu phẩy động biểu thị các giá trị dấu phẩy động thông qua các chuỗi sử dụng ký hiệu thập phân hoặc khoa học. Ngoài ra, bạn có thể dùng ký hiệu thập lục phân để chỉ định trực tiếp các bit cơ bản ở định dạng dấu phẩy động của loại tương ứng. Hằng số dấu phẩy động có các ràng buộc sau:
- (C1) Nếu bạn sử dụng ký hiệu không phải thập lục phân, hãy dùng
is_wellformed(float_literal, float_type). - (C2) Nếu sử dụng ký hiệu thập lục phân, hãy dùng
size(hexadecimal_digits) = num_bits(float_type) / 4.
ComplexConstant ::= ComplexLiteral ':' ComplexType
ComplexLiteral ::= '(' RealPart ',' ImaginaryPart ')'
RealPart ::= FloatLiteral
ImaginaryPart ::= FloatLiteral
Hằng số phức biểu thị các giá trị phức bằng cách sử dụng danh sách gồm phần thực (xuất hiện đầu tiên) và phần ảo (xuất hiện thứ hai). Ví dụ: (1.0, 0.0) : complex<f32> biểu thị 1.0 + 0.0i và (0.0, 1.0) : complex<f32> biểu thị 0.0 + 1.0i. Thứ tự mà các phần này được lưu trữ trong bộ nhớ là do quá trình triển khai xác định. Hằng số phức có các ràng buộc sau:
- (C1)
is_wellformed(real_part, complex_element_type(complex_type)). - (C2)
is_wellformed(imaginary_part, complex_element_type(complex_type)).
TensorConstant ::= TensorLiteral ':' TensorType
TensorLiteral ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'
DenseLiteral ::= DenseDimension | DenseElements
DenseDimension ::= '[' [DenseLiteral {',' DenseLiteral}] ']'
DenseElements ::= [ElementLiteral {',' ElementLiteral}]
ElementLiteral ::= BooleanLiteral | IntegerLiteral | FloatLiteral | ComplexLiteral
Hằng số tensor biểu thị các giá trị tensor bằng cách sử dụng các danh sách lồng nhau được chỉ định thông qua ký pháp NumPy. Ví dụ: dense<[[1, 2, 3], [4, 5, 6]]> : tensor<2x3xi32> biểu thị một giá trị tensor có ánh xạ sau đây từ chỉ mục đến các phần tử: {0, 0} => 1, {0, 1} => 2, {0, 2} => 3, {1, 0} => 4, {1, 1} => 5, {1, 2} => 6. Thứ tự mà các phần tử này được lưu trữ trong bộ nhớ sau đó sẽ do quá trình triển khai xác định. Hằng số Tensor có các ràng buộc sau:
- (C1)
has_syntax(tensor_literal, element_type(tensor_type)), trong đó:has_syntax(element_literal: Syntax, element_type: Type) = is_wellformed(element_literal, type).has_syntax(tensor_literal: List, element_type: Type) = has_syntax(tensor_literal..., element_type).
- (C2)
has_shape(tensor_literal, shape(tensor_type)), trong đó:has_shape(element_literal: Syntax, []) = true.has_shape(tensor_literal: List, shape: List) = size(tensor_literal) = shape[0] and has_shape(tensor_literal..., shape[1:]).- nếu không, hãy nói
false.
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'
Hằng số tensor được lượng tử hoá biểu thị các giá trị tensor được lượng tử hoá bằng cách sử dụng cùng một ký hiệu như hằng số tensor, với các phần tử được chỉ định là hằng số của loại lưu trữ. Các hằng số tensor được lượng tử hoá có các ràng buộc sau:
- (C1)
has_syntax(quantized_tensor_literal, storage_type(quantized_tensor_type)). - (C2)
has_shape(quantized_tensor_literal, shape(quantized_tensor_type)).
StringConstant ::= StringLiteral
StringLiteral ::= '"' {stringCharacter | escapeSequence} '"'
stringCharacter ::= all ASCII characters except '\00', '\01', ... '\1f' and '"'
escapeSequence ::= '\' ('"' | '\' | 'n' | 't' | (hexadecimalDigit hexadecimalDigit))
Chuỗi ký tự bao gồm các byte được chỉ định bằng cách sử dụng ký tự ASCII và chuỗi thoát. Chúng không phụ thuộc vào phương thức mã hoá, vì vậy, cách diễn giải các byte này do quá trình triển khai xác định. Hằng chuỗi có loại string.
Ops
Giá trị tuyệt đối
Ngữ nghĩa
Thực hiện phép toán abs theo phần tử trên tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số nguyên có dấu: mô đun số nguyên.
- Đối với số thực:
abstừ IEEE-754. - Đối với số phức: mô-đun phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(abs, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor của số nguyên có dấu, dấu phẩy động hoặc loại phức hợp hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên có dấu hoặc dấu phẩy động hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C2) |
Giới hạn
- (C1)
shape(result) = shape(operand). - (C2)
baseline_element_type(result)được xác định là:complex_element_type(element_type(operand))nếuis_complex(operand).- Nếu không thì là
baseline_element_type(operand).
Ví dụ
// %operand: [-2, 0, 2]
%result = "stablehlo.abs"(%operand) : (tensor<3xi32>) -> tensor<3xi32>
// %result: [2, 0, 2]
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 giá trị boolean: OR logic.
- Đối với số nguyên: phép cộng số nguyên.
- Đối với số thực:
additiontừ IEEE-754. - Đối với số phức: phép cộng số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(add, lhs, rhs, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor hoặc tensor được lượng tử hoá | (C1-C6) |
| (I2) | rhs |
tensor hoặc tensor được lượng tử hoá | (C1-C5), (C7) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá | (C1 – C7) |
Giới hạn
- Nếu thao tác sử dụng các tensor không được định lượng:
- (C1)
type(lhs) = type(rhs) = type(result).
- (C1)
- Nếu thao tác sử dụng các tensor được định lượng:
- (C2)
is_quantized(lhs) and is_quantized(rhs) and is_quantized(result). - (C3)
storage_type(lhs) = storage_type(rhs) = storage_type(result). - (C4)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result). - (C5)
(is_per_axis_quantized(lhs) or is_per_axis_quantized(rhs)) = is_per_axis_quantized(result). - (C6) Nếu
is_per_axis_quantized(lhs), thìquantization_dimension(lhs) = quantization_dimension(result). - (C7) Nếu
is_per_axis_quantized(rhs), thìquantization_dimension(rhs) = quantization_dimension(result).
- (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ả, nó chỉ tồn tại để thiết lập các phần phụ thuộc dữ liệu từ result đến inputs.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại |
|---|---|---|
| (I1) | inputs |
số lượng token có thể thay đổi |
Kết quả đầu ra
| Tên | Loại |
|---|---|
result |
token |
Ví dụ
// %input0: !stablehlo.token
// %input1: !stablehlo.token
%result = "stablehlo.after_all"(%input0, %input1) : (!stablehlo.token, !stablehlo.token) -> !stablehlo.token
all_gather
Ngữ nghĩa
Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy nối các giá trị của tensor operands từ mỗi quy trình dọc theo all_gather_dim và tạo ra các tensor results.
Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:
cross_replica(replica_groups)nếuchannel_id <= 0 and use_global_device_ids = false.cross_replica_and_partition(replica_groups)nếuchannel_id > 0 and use_global_device_ids = false.flattened_ids(replica_groups)nếuchannel_id > 0 and use_global_device_ids = true.
Sau đó, trong mỗi process_group:
operands...@receiver = [operand@sender for sender in process_group]cho tất cảreceivertrongprocess_group.results...@process = concatenate(operands...@process, all_gather_dim)cho tất cảprocesstrongprocess_group.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operands |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C6) |
| (I2) | all_gather_dim |
hằng số thuộc loại si64 |
(C1), (C6) |
| (I3) | replica_groups |
Hằng số tensor 2 chiều thuộc loại si64 |
(C2-C4) |
| (I4) | channel_id |
hằng số thuộc loại si64 |
(C5) |
| (I5) | use_global_device_ids |
hằng số thuộc loại i1 |
(C5) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C6) |
Giới hạn
- (C1)
0 <= all_gather_dim < rank(operands...). - (C2)
is_unique(replica_groups). - (C3)
size(replica_groups)được xác định là:num_replicasnếu bạn sử dụngcross_replica.num_replicasnếu bạn sử dụngcross_replica_and_partition.num_processesnếu bạn sử dụngflattened_ids.
- (C4)
0 <= replica_groups < size(replica_groups). - (C5) Nếu
use_global_device_ids = true, thìchannel_id > 0. - (C6)
type(results...) = type(operands...)ngoại trừ:dim(results..., all_gather_dim) = dim(operands..., all_gather_dim) * dim(process_groups, 1).
Ví dụ
// num_replicas: 2
// num_partitions: 1
// %operand0@(0, 0): [[1, 2], [3, 4]]
// %operand0@(1, 0): [[5, 6], [7, 8]]
// %operand1@(0, 0): [[11, 12], [13, 14]]
// %operand1@(1, 0): [[15, 16], [17, 18]]
%result:2 = "stablehlo.all_gather"(%operand0, %operand1) {
all_gather_dim = 1 : i64,
replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
// channel_id = 0
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
// use_global_device_ids = false
} : (tensor<2x2xi64>, tensor<2x2xi64>) -> (tensor<2x4xi64>, tensor<2x4xi64>)
// %result0@(0, 0): [[1, 2, 5, 6], [3, 4, 7, 8]]
// %result0@(1, 0): [[1, 2, 5, 6], [3, 4, 7, 8]]
// %result1@(0, 0): [[11, 12, 15, 16], [13, 14, 17, 18]]
// %result1@(1, 0): [[11, 12, 15, 16], [13, 14, 17, 18]]
all_reduce
Ngữ nghĩa
Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy áp dụng một hàm rút gọn computation cho các giá trị của tensor operands từ mỗi quy trình và tạo ra các tensor results.
Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:
cross_replica(replica_groups)nế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ânscheduletrong đó:exec(node)=computation(exec(node.left), exec(node.right)).exec(leaf)=leaf.value.
schedulelà một cây nhị phân do quá trình triển khai xác định, trong đó thứ tự duyệt làto_destination_type(operands...@process_group...[result_index], type(func_inputs(computation)[0])).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operands |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C5), (C6) |
| (I2) | replica_groups |
số lượng biến đổi của hằng số tensor 1 chiều thuộc loại si64 |
(C1-C3) |
| (I3) | channel_id |
hằng số thuộc loại si64 |
(C4) |
| (I4) | use_global_device_ids |
hằng số thuộc loại i1 |
(C4) |
| (I5) | computation |
hàm | (C5) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C6-C7) |
Giới hạn
- (C1)
is_unique(replica_groups). - (C2)
size(replica_groups)được xác định là:num_replicasnếu bạn sử dụngcross_replica.num_replicasnếu bạn sử dụngcross_replica_and_partition.num_processesnếu bạn sử dụngflattened_ids.
- (C3)
0 <= replica_groups < size(replica_groups). - (C4) Nếu
use_global_device_ids = true, thìchannel_id > 0. - (C5)
computationcó loại(tensor<E>, tensor<E>) -> (tensor<E>)trong đóis_promotable(element_type(operand), E). - (C6)
shape(results...) = shape(operands...). - (C7)
element_type(results...) = E.
Ví dụ
// num_replicas: 2
// num_partitions: 1
// %operand0@(0, 0): [1, 2, 3, 4]
// %operand0@(1, 0): [5, 6, 7, 8]
// %operand1@(0, 0): [9, 10, 11, 12]
// %operand1@(1, 0): [13, 14, 15, 16]
%result:2 = "stablehlo.all_reduce"(%operand0, %operand0) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
// channel_id = 0
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
// use_global_device_ids = false
} : (tensor<4xi64>, tensor<4xi64>) -> (tensor<4xi64>, tensor<4xi64>)
// %result0@(0, 0): [6, 8, 10, 12]
// %result0@(1, 0): [6, 8, 10, 12]
// %result1@(0, 0): [22, 24, 26, 28]
// %result1@(1, 0): [22, 24, 26, 28]
all_to_all
Ngữ nghĩa
Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy chia các giá trị của tensor operands dọc theo split_dimension thành các phần, phân tán các phần đã chia giữa các quy trình, nối các phần đã phân tán dọc theo concat_dimension và tạo ra các tensor results.
Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:
cross_replica(replica_groups)nếuchannel_id <= 0.cross_partition(replica_groups)nếuchannel_id > 0.
Sau đó, trong mỗi process_group:
split_parts...@sender = split(operands...@sender, split_count, split_dimension)cho tất cảsendertrongprocess_group.scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group]nơireceiver_index = process_group.index(receiver).results...@process = concatenate(scattered_parts...@process, concat_dimension).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operands |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C1 – C3), (C9) |
| (I2) | split_dimension |
hằng số thuộc loại si64 |
(C1), (C2), (C9) |
| (I3) | concat_dimension |
hằng số thuộc loại si64 |
(C3), (C9) |
| (I4) | split_count |
hằng số thuộc loại si64 |
(C2), (C4), (C8), (C9) |
| (I5) | replica_groups |
Hằng số tensor 2 chiều thuộc loại si64 |
(C5-C8) |
| (I6) | channel_id |
hằng số thuộc loại si64 |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C9) |
Giới hạn
- (C1)
0 <= split_dimension < rank(operands...). - (C2)
dim(operands..., split_dimension) % split_count = 0. - (C3)
0 <= concat_dimension < rank(operands...). - (C4)
0 < split_count. - (C5)
is_unique(replica_groups). - (C6)
size(replica_groups)được xác định là:num_replicasnếu bạn sử dụngcross_replica.num_partitionsnếu bạn sử 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ừ trường hợpsplit_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 toán 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 giá trị boolean: phép toán AND logic.
- Đối với số nguyên: bitwise AND.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại boolean hoặc số nguyên | (C1) |
| (I2) | rhs |
tensor thuộc loại boolean hoặc số nguyên | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại boolean hoặc số nguyên | (C1) |
Giới hạn
- (C1)
type(lhs) = type(rhs) = type(result).
Ví dụ
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.and"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[1, 2], [3, 0]]
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 tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
atan2từ IEEE-754. - Đối với số phức: atan2 phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(atan2, lhs, rhs, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
| (I2) | rhs |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).
Ví dụ
// %lhs: [0.0, 1.0, -1.0]
// %rhs: [0.0, 0.0, 0.0]
%result = "stablehlo.atan2"(%lhs, %rhs) : (tensor<3xf64>, tensor<3xf64>) -> tensor<3xf64>
// %result: [0.0, 1.57079637, -1.57079637] // [0.0, pi/2, -pi/2]
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. Cụ thể hơn, thao tác này có thể được biểu thị dưới dạng một thao tác phân tách thành các thao tác StableHLO hiện có bằng cách sử dụng cú pháp Python như sau:
def compute_sum(operand, feature_index):
(sum,) = reduce(
inputs=[operand],
init_values=[constant(0, element_type(operand))],
dimensions=[i for i in range(rank(operand)) if i != feature_index],
body=lambda x, y: add(x, y))
return sum
def compute_mean(operand, feature_index):
sum = compute_sum(operand, feature_index)
divisor = constant(size(operand) / dim(operand, feature_index),
element_type(operand))
divisor_bcast = broadcast_in_dim(divisor, [], type(sum))
return divide(sum, divisor_bcast)
def batch_norm_grad(operand, scale, mean, variance, grad_output, epsilon, feature_index):
# Broadcast inputs to type(operand)
scale_bcast = broadcast_in_dim(scale, [feature_index], type(operand))
mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
variance_bcast = broadcast_in_dim(variance, [feature_index], type(operand))
epsilon_bcast = broadcast_in_dim(constant(epsilon, element_type(operand)), [],
type(operand))
# Perform normalization using the provided `mean` and `variance`
# Intermediate values will be useful for computing gradients
centered_operand = subtract(operand, mean_bcast)
stddev = sqrt(add(variance_bcast, epsilon_bcast))
normalized_operand = divide(centered_operand, stddev)
# Use the implementation from batchnorm_expander.cc in XLA
# Temporary variables have exactly the same names as in the C++ code
elements_per_feature = broadcast_in_dim(
constant(divide(size(operand), dim(operand, feature_index)),
element_type(grad_output)),
[], type(operand))
i1 = multiply(grad_output, elements_per_feature)
i2 = broadcast_in_dim(
compute_sum(grad_output, feature_index), [feature_index], type(operand))
i3 = broadcast_in_dim(
compute_sum(multiply(grad_output, centered_operand), feature_index),
[feature_index], type(operand))
i4 = multiply(i3, centered_operand)
i5 = divide(i4, add(variance_bcast, epsilon_bcast))
i6 = subtract(subtract(i1, i2), i5)
grad_operand =
multiply(divide(divide(scale_bcast, stddev), elements_per_feature), i6)
grad_scale =
compute_sum(multiply(grad_output, normalized_operand), feature_index)
grad_offset = compute_sum(grad_output, feature_index)
return grad_operand, grad_scale, grad_offset
Đối với các loại được định lượng, hãy thực hiện dequantize_batch_norm_grad_or_training_quantize(lambda operand, scale, mean,
variance, grad_output: batch_norm_grad(operand, scale, mean, variance,
grad_output, epsilon, feature_index), operand, scale, mean, variance,
grad_output, type(grad_operand), type(grad_scale), type(feature_index)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1-C3), (C5) |
| (I2) | scale |
Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor | (C2), (C4), (C5) |
| (I3) | mean |
Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor | (C2), (C4) |
| (I4) | variance |
Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor | (C2), (C4) |
| (I5) | grad_output |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C2), (C3) |
| (I6) | epsilon |
hằng số thuộc loại f32 |
|
| (I7) | feature_index |
hằng số thuộc loại si64 |
(C1), (C5) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
grad_operand |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C2), (C3) |
grad_scale |
Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor | (C2), (C4) |
grad_offset |
Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor | (C2), (C4) |
Giới hạn
- (C1)
0 <= feature_index < rank(operand). - (C2)
operand,scale,mean,variance,grad_output,grad_operand,grad_scalevàgrad_offsetcó cùngbaseline_element_type. - (C3)
operand,grad_outputvàgrad_operandcó cùng hình dạng. - (C4)
scale,mean,variance,grad_scalevàgrad_offsetcó cùng hình dạng. - (C5)
size(scale) = dim(operand, feature_index).
Ví dụ
// %operand: [
// [[1.0, 2.0], [3.0, 4.0]],
// [[3.0, 4.0], [1.0, 2.0]]
// ]
// %scale: [1.0, 1.0]
// %mean: [2.0, 3.0]
// %variance: [1.0, 1.0]
// %grad_output: [
// [[0.1, 0.1], [0.1, 0.1]],
// [[0.1, 0.1], [0.1, 0.1]]
// ]
%grad_operand, %grad_scale, %grad_offset =
"stablehlo.batch_norm_grad"(%operand, %scale, %mean, %variance, %grad_output) {
epsilon = 0.0 : f32,
feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>,
tensor<2x2x2xf64>) -> (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %grad_operand: [
// [[0.0, 0.0], [0.0, 0.0]],
// [[0.0, 0.0], [0.0, 0.0]]
// ]
// %grad_scale: [0.0, 0.0]
// %grad_offset: [0.4, 0.4]
batch_norm_inference
Ngữ nghĩa
Chuẩn hoá tensor operand trên tất cả các phương diện, ngoại trừ phương diện feature_index và tạo ra một tensor result. Theo cách chính thức hơn, thao tác này có thể được biểu thị dưới dạng một thao tác phân tách thành các thao tác StableHLO hiện có bằng cách sử dụng cú pháp Python như sau:
def batch_norm_inference(operand, scale, offset, mean, variance, epsilon, feature_index):
# Broadcast inputs to shape(operand)
scale_bcast = broadcast_in_dim(scale, [feature_index], type(operand))
offset_bcast = broadcast_in_dim(offset, [feature_index], type(operand))
mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
variance_bcast = broadcast_in_dim(variance, [feature_index], type(operand))
epsilon_bcast = broadcast_in_dim(constant(epsilon, element_type(operand)), [],
type(operand))
# Perform normalization using the provided `mean` and `variance` instead of
# computing them like `batch_norm_training` does.
centered_operand = subtract(operand, mean_bcast)
stddev = sqrt(add(variance_bcast, epsilon_bcast))
normalized_operand = divide(centered_operand, stddev)
return add(multiply(scale_bcast, normalized_operand), offset_bcast)
Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(lambda operand, scale, offset, mean, variance:
batch_norm_inference(operand, scale, offset, mean, variance, epsilon,
feature_index), operand, scale, offset, mean, variance, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1 – C7) |
| (I2) | scale |
Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor | (C2), (C3) |
| (I3) | offset |
Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor | (C2), (C4) |
| (I4) | mean |
Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor | (C5) |
| (I5) | variance |
Tensor 1 chiều có kiểu lượng tử hoá dấu phẩy động hoặc trên mỗi tensor | (C2), (C6) |
| (I6) | epsilon |
hằng số thuộc loại f32 |
|
| (I7) | feature_index |
hằng số thuộc loại si64 |
(C1), (C3-C6) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C2), (C7) |
Giới hạn
- (C1)
0 <= feature_index < rank(operand). - (C2)
operand,scale,offset,mean,variancevàresultcó cùngbaseline_element_type. - (C3)
size(scale) = dim(operand, feature_index). - (C4)
size(offset) = dim(operand, feature_index). - (C5)
size(mean) = dim(operand, feature_index). - (C6)
size(variance) = dim(operand, feature_index). - (C7)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [
// [[1.0, 2.0], [3.0, 4.0]],
// [[3.0, 4.0], [1.0, 2.0]]
// ]
// %scale: [1.0, 1.0]
// %offset: [1.0, 1.0]
// %mean: [2.0, 3.0]
// %variance: [1.0, 1.0]
%result = "stablehlo.batch_norm_inference"(%operand, %scale, %offset, %mean, %variance) {
epsilon = 0.0 : f32,
feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>) -> tensor<2x2x2xf64>
// %result: [
// [[0.0, 0.0], [2.0, 2.0]],
// [[2.0, 2.0], [0.0, 0.0]]
// ]
batch_norm_training
Ngữ nghĩa
Tính toán giá trị trung bình và phương sai trên tất cả các phương diện, ngoại trừ phương diện feature_index và chuẩn hoá tenxơ operand tạo ra các tenxơ output, batch_mean và batch_var. Cụ thể hơn, bạn có thể biểu thị thao tác này dưới dạng một thao tác phân tách thành các thao tác StableHLO hiện có bằng cách sử dụng cú pháp Python như sau:
def compute_mean(operand, feature_index):
(sum,) = reduce(
inputs=[operand],
init_values=[constant(0, element_type(operand))],
dimensions=[i for i in range(rank(operand)) if i != feature_index],
body=lambda x, y: add(x, y))
divisor = constant(size(operand) / dim(operand, feature_index),
element_type(operand))
divisor_bcast = broadcast_in_dim(divisor, [], type(sum))
return divide(sum, divisor_bcast)
def compute_variance(operand, feature_index):
mean = compute_mean(operand, feature_index)
mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
centered_operand = subtract(operand, mean_bcast)
return compute_mean(mul(centered_operand, centered_operand), feature_index)
def batch_norm_training(operand, scale, offset, epsilon, feature_index):
mean = compute_mean(operand, feature_index)
variance = compute_variance(operand, feature_index)
return batch_norm_inference(operand, scale, offset, mean, variance, epsilon,
feature_index),
mean, variance
Đối với các loại được định lượng, hãy thực hiện dequantize_batch_norm_grad_or_training_quantize(lambda operand, scale, offset:
batch_norm_training(operand, scale, offset, epsilon, feature_index), operand,
scale, offset, type(output), type(batch_mean), type(batch_var)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
| (I2) | scale |
Tensor 1 chiều của dấu phẩy động hoặc được lượng tử hoá theo từng tensor | (C2), (C3) |
| (I3) | offset |
Tensor 1 chiều của dấu phẩy động hoặc được lượng tử hoá theo từng tensor | (C2), (C4) |
| (I4) | epsilon |
hằng số thuộc loại f32 |
(C1), (C3-C6) |
| (I5) | feature_index |
hằng số thuộc loại si64 |
(C1), (C3-C6) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
output |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C7) |
batch_mean |
Tensor 1 chiều của dấu phẩy động hoặc được lượng tử hoá theo từng tensor | (C2), (C5) |
batch_var |
Tensor 1 chiều của dấu phẩy động hoặc được lượng tử hoá theo từng tensor | (C2), (C6) |
Giới hạn
- (C1)
0 <= feature_index < rank(operand). - (C2)
operand,scale,offset,batch_mean,batch_varvàoutputcó cùngbaseline_element_type. - (C3)
size(scale) = dim(operand, feature_index). - (C4)
size(offset) = dim(operand, feature_index). - (C5)
size(batch_mean) = dim(operand, feature_index). - (C6)
size(batch_var) = dim(operand, feature_index). - (C7)
baseline_type(output) = baseline_type(operand).
Ví dụ
// %operand: [
// [[1.0, 2.0], [3.0, 4.0]],
// [[3.0, 4.0], [1.0, 2.0]]
// ]
// %scale: [1.0, 1.0]
// %offset: [1.0, 1.0]
%output, %batch_mean, %batch_var = "stablehlo.batch_norm_training"(%operand, %scale, %offset) {
epsilon = 0.0 : f32,
feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>) ->
(tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %output: [
// [[0.0, 0.0], [2.0, 2.0]],
// [[2.0, 2.0], [0.0, 0.0]]
// ]
// %batch_mean: [2.0, 3.0]
// %batch_var: [1.0, 1.0]
bitcast_convert
Ngữ nghĩa
Thực hiện một thao tác bitcast trên tenxơ operand và tạo ra một tenxơ result, trong đó các bit của toàn bộ tenxơ operand được diễn giải lại bằng cách sử dụng kiểu của tenxơ result.
Cụ thể hơn, cho E = element_type(operand), E' = element_type(result) và R = rank(operand):
- Nếu
num_bits(E') < num_bits(E),bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1]). - Nếu
num_bits(E') > num_bits(E),bits(result[i0, ..., iR-2]) = bits(operand[i0, ..., iR-2, :]). - Nếu
num_bits(E') = num_bits(E),bits(result[i0, ..., iR-1]) = bits(operand[i0, ..., iR-1]).
bits trả về biểu thị trong bộ nhớ của một giá trị nhất định và hành vi của giá trị này được xác định theo cách triển khai vì biểu thị chính xác của các tensor được xác định theo cách triển khai và biểu thị chính xác của các loại phần tử cũng được xác định theo cách triển khai.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor được lượng tử hoá | (C1-C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá | (C1-C2) |
Giới hạn
- (C1) Cho
E = is_quantized(operand) ? storage_type(operand) : element_type(operand),E' = is_quantized(result) ? storage_type(result) : element_type(result)vàR = rank(operand):- Nếu
num_bits(E') = num_bits(E),shape(result) = shape(operand). - Nếu
num_bits(E') < num_bits(E): rank(result) = R + 1.dim(result, i) = dim(operand, i)cho tất cả0 <= i < R.dim(result, R) * num_bits(E') = num_bits(E).- Nếu
num_bits(E') > num_bits(E): rank(result) = R - 1.dim(result, i) = dim(operand, i)cho tất cả0 <= i < R.dim(operand, R - 1) * num_bits(E) = num_bits(E').
- Nếu
- (C2) Nếu
is_complex(operand) or is_complex(result), thìis_complex(operand) and is_complex(result).
Ví dụ
// %operand: 0x0123456789ABCDEF
%result = "stablehlo.bitcast_convert"(%operand) : (tensor<f64>) -> tensor<4xf16>
// %result: [0xCDEF, 0x89AB, 0x4567, 0x0123] // little-endian representation
broadcast_in_dim
Ngữ nghĩa
Mở rộng phương diện và/hoặc thứ hạng của một tensor đầu vào bằng cách sao chép dữ liệu trong tensor operand và tạo ra một tensor result. Cụ thể hơn, result[result_index] = operand[operand_index] trong đó với mọi d trong axes(operand):
operand_index[d] = 0nếudim(operand, d) = 1.- Nếu không thì là
operand_index[d] = result_index[broadcast_dimensions[d]].
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor được lượng tử hoá | (C1-C2), (C5-C6) |
| (I2) | broadcast_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2-C6) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá | (C1), (C3), (C5-C6) |
Giới hạn
- (C1)
element_type(result)được cho bởi:element_type(operand), nếu!is_per_axis_quantized(operand).element_type(operand)ngoại trừquantization_dimension(operand),scales(operand)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ởaxes(operand):dim(operand, d) = 1hoặcdim(operand, d) = dim(result, broadcast_dimensions[d]).
- (C6) Nếu
is_per_axis_quantized(result):quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)].- Nếu
dim(operand, quantization_dimension(operand)) = 1, thìscales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result))).
Ví dụ
// %operand: [
// [1, 2, 3]
// ]
%result = "stablehlo.broadcast_in_dim"(%operand) {
broadcast_dimensions = array<i64: 2, 1>
} : (tensor<1x3xi32>) -> tensor<2x3x2xi32>
// %result: [
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ],
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ]
// ]
ốp lưng
Ngữ nghĩa
Tạo ra kết quả đầu ra từ việc thực thi chính xác một hàm từ branches tuỳ thuộc vào giá trị của index. Cụ thể hơn, result = selected_branch()
trong đó:
selected_branch = branches[index]nếu0 <= index < size(branches).- Nếu không thì là
selected_branch = branches[-1].
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | index |
Tensor 0 chiều thuộc loại si32 |
|
| (I2) | branches |
số lượng hàm có thể thay đổi | (C1-C4) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor, tensor lượng tử hoá hoặc mã thông báo có thể thay đổi | (C4) |
Giới hạn
- (C1)
0 < size(branches). - (C2)
input_types(branches...) = []. - (C3)
same(output_types(branches...)). - (C4)
type(results...) = output_types(branches[0]).
Ví dụ
// %index: -1
// %result_branch0: [0, 0]
// %result_branch1: [1, 1]
%result0, %result1 = "stablehlo.case"(%index) ({
"stablehlo.return"(%result_branch0, %result_branch0) : (tensor<2xi64>, tensor<2xi64>) -> ()
}, {
"stablehlo.return"(%result_branch1, %result_branch1) : (tensor<2xi64>, tensor<2xi64>) -> ()
}) : (tensor<i32>) -> (tensor<2xi64>, tensor<2xi64>)
// %result0: [1, 1]
// %result1: [1, 1]
cbrt
Ngữ nghĩa
Thực hiện phép toán căn bậc ba theo phần tử trên tensor operand và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
rootn(x, 3)từ IEEE-754. - Đối với số phức: căn bậc ba của số phức.
- Đối với các loại được định lượng:
dequantize_op_quantize(cbrt, operand, type(result))
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [0.0, 1.0, 8.0, 27.0]
%result = "stablehlo.cbrt"(%operand) : (tensor<4xf64>) -> tensor<4xf64>
// %result: [0.0, 1.0, 2.0, 3.0]
ceil
Ngữ nghĩa
Thực hiện thao tác làm tròn lên từng phần tử của tensor operand và tạo ra một tensor result.
Triển khai thao tác roundToIntegralTowardPositive theo quy cách IEEE-754. Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(ceil, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [-0.8166, -0.2530, 0.2530, 0.8166, 2.0]
%result = "stablehlo.ceil"(%operand) : (tensor<5xf32>) -> tensor<5xf32>
// %result: [-0.0, -0.0, 1.0, 1.0, 2.0]
cholesky
Ngữ nghĩa
Tính toán phân tích Cholesky của một lô ma trận.
Cụ thể hơn, đối với tất cả i trong index_space(result), result[i0, ..., iR-3, :, :] là một phép phân tích Cholesky của a[i0, ..., iR-3, :, :], dưới dạng ma trận tam giác dưới (nếu lower là true) hoặc ma trận 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 theo cách triển khai.
Nếu có i trong đó ma trận đầu vào không phải là ma trận xác định dương Hermitian, thì hành vi này không xác định.
Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | a |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C3) |
| (I2) | lower |
hằng số thuộc loại i1 |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(a) = baseline_type(result). - (C2)
2 <= rank(a). - (C3)
dim(a, -2) = dim(a, -1).
Ví dụ
// %a: [
// [1.0, 2.0, 3.0],
// [2.0, 20.0, 26.0],
// [3.0, 26.0, 70.0]
// ]
%result = "stablehlo.cholesky"(%a) {
lower = true
} : (tensor<3x3xf32>) -> tensor<3x3xf64>
// %result: [
// [1.0, 0.0, 0.0],
// [2.0, 4.0, 0.0],
// [3.0, 5.0, 6.0]
// ]
kẹp
Ngữ nghĩa
Kẹp mọi phần tử của tensor operand trong khoảng từ giá trị tối thiểu đến tối đa và tạo ra tensor result. Cụ thể hơn, result[result_index] =
minimum(maximum(operand[result_index], min_element), max_element), trong đó min_element = rank(min) = 0 ? min[] : min[result_index], max_element = rank(max) = 0 ? max[] : max[result_index]. Đối với các loại được lượng tử hoá, hãy thực hiện dequantize_op_quantize(clamp, min, operand, max, type(result)).
Việc áp đặt một thứ tự cho các số phức liên quan đến ngữ nghĩa đáng ngạc nhiên, vì vậy trong tương lai, chúng tôi dự định xoá hỗ trợ cho các số phức đối với thao tác này (#560).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | min |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C3) |
| (I2) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C4) |
| (I3) | max |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C2), (C3) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C4) |
Giới hạn
- (C1)
rank(min) = 0 or shape(min) = shape(operand). - (C2)
rank(max) = 0 or shape(max) = shape(operand). - (C3)
baseline_element_type(min) = baseline_element_type(operand) = baseline_element_type(max). - (C4)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %min: [5, 10, 15]
// %operand: [3, 13, 23]
// %max: [10, 15, 20]
%result = "stablehlo.clamp"(%min, %operand, %max) : (tensor<3xi32>, tensor<3xi32>, tensor<3xi32>) -> tensor<3xi32>
// %result: [5, 13, 20]
collective_broadcast
Ngữ nghĩa
Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy gửi giá trị của tensor operand từ quy trình nguồn đến các quy trình đích và tạo ra một tensor result.
Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:
cross_replica(replica_groups)nếuchannel_id <= 0.cross_partition(replica_groups)nếuchannel_id > 0.
Sau đó, result@process được tính theo công thức:
operand@process_groups[i, 0]nếu có mộtisao cho quy 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 được lượng tử hoá theo từng tensor | (C3) |
| (I2) | replica_groups |
số lượng biến đổi của hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C2) |
| (I3) | channel_id |
hằng số thuộc loại si64 |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C3) |
Giới hạn
- (C1)
is_unique(replica_groups). - (C2)
0 <= replica_groups < Ntrong đóNđược xác định là:num_replicasnếu bạn sử dụngcross_replica.num_partitionsnếu bạn sử dụngcross_partition.
- (C3)
type(result) = type(operand).
Ví dụ
// num_replicas: 4
// num_partitions: 1
// %operand@(0, 0): [[1, 2]]
// %operand@(1, 0): [[3, 4]]
// %operand@(2, 0): [[5, 6]]
// %operand@(3, 0): [[7, 8]]
%result = "stablehlo.collective_broadcast"(%operand) {
replica_groups = dense<[[2, 1]]> : tensor<1x2xi64>,
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor1x2xi64>) -> tensor<1x2xi64>
// %result@(0, 0): [[0, 0]]
// %result@(1, 0): [[5, 6]]
// %result@(2, 0): [[5, 6]]
// %result@(3, 0): [[0, 0]]
collective_permute
Ngữ nghĩa
Trong mỗi nhóm quy trình trong lưới quy trình StableHLO, hãy gửi giá trị của tensor operand từ quy trình nguồn đến quy trình đích và tạo ra một tensor result.
Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:
cross_replica(source_target_pairs)nếuchannel_id <= 0.cross_partition(source_target_pairs)nếuchannel_id > 0.
Sau đó, result@process được tính theo công thức:
operand@process_groups[i, 0], nếu tồn tại mộtisao choprocess_groups[i, 1] = process.broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result))nếu không.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C5) |
| (I2) | source_target_pairs |
Hằng số tensor 2 chiều thuộc loại si64 |
(C1-C4) |
| (I3) | channel_id |
hằng số thuộc loại si64 |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
dim(source_target_pairs, 1) = 2. - (C2)
is_unique(source_target_pairs[:, 0]). - (C3)
is_unique(source_target_pairs[:, 1]). - (C4)
0 <= source_target_pairs < N, trong đóNđược xác định là:num_replicasnếu bạn sử dụngcross_replica.num_partitionsnếu bạn sử 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 từng 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.
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, thao tác này sẽ triển khai các thao tác IEEE-754 sau:
EQ:compareQuietEqual.NE:compareQuietNotEqual.GE:compareQuietGreaterEqual.GT:compareQuietGreater.LE:compareQuietLessEqual.LT:compareQuietLess.
Đối với các kiểu phần tử dấu phẩy động có compare_type = TOTALORDER, thao tác này sử dụng tổ hợp các thao tác totalOrder và compareQuietEqual từ IEEE-754.
Đối với các loại phần tử phức tạp, việc so sánh theo từ điển các cặp (real, imag) được thực hiện bằng cách sử dụng comparison_direction và compare_type được cung cấp.
Việc áp đặt một thứ tự cho các số phức liên quan đến ngữ nghĩa đáng ngạc nhiên, vì vậy trong tương lai, chúng tôi dự định sẽ xoá hỗ trợ cho các số phức khi comparison_direction là GE, GT, LE hoặc LT (#560).
Đối với các loại được định lượng, hãy thực hiện dequantize_compare(lhs, rhs,
comparison_direction).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1-C3) |
| (I2) | rhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1-C2) |
| (I3) | comparison_direction |
enum của EQ, NE, GE, GT, LE và LT |
|
| (I4) | compare_type |
enum của FLOAT, TOTALORDER, SIGNED và UNSIGNED |
(C3) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại boolean | (C2) |
Giới hạn
- (C1)
baseline_element_type(lhs) = baseline_element_type(rhs). - (C2)
shape(lhs) = shape(rhs) = shape(result). - (C3)
compare_typeđược xác định là:SIGNEDnếuis_signed_integer(element_type(lhs)).UNSIGNEDnếuis_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs)).FLOAThoặcTOTALORDERnếuis_float(element_type(lhs)).FLOATnếuis_complex(element_type(lhs)).
Ví dụ
// %lhs: [1.0, 3.0]
// %rhs: [1.1, 2.9]
%result = "stablehlo.compare"(%lhs, %rhs) {
comparison_direction = #stablehlo<comparison_direction LT>,
compare_type = #stablehlo<comparison_type FLOAT>
} : (tensor<2xf32>, tensor<2xf32>) -> tensor<2xi1>
// %result: [true, false]
phức tạp
Ngữ nghĩa
Thực hiện quá trình chuyển đổi theo phần tử thành một giá trị phức tạp từ một cặp giá trị thực và giá trị ảo, lhs và rhs, đồng thời tạo ra một tenxơ result.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại f32 hoặc f64 |
(C1-C3) |
| (I2) | rhs |
tensor thuộc loại f32 hoặc f64 |
(C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại phức | (C2), (C3) |
Giới hạn
- (C1)
type(lhs) = type(rhs). - (C2)
shape(result) = shape(lhs). - (C3)
element_type(result)có loạ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 thao tác được tạo thành từ các thao tác StableHLO khác, lấy inputs và composite_attributes, đồng thời tạo ra results. Ngữ nghĩa của thao tác được triển khai bằng thuộc tính decomposition. Bạn có thể thay thế thao tác composite bằng thao tác phân tách mà không cần thay đổi ngữ nghĩa của chương trình. Trong trường hợp việc nội tuyến quá trình phân tách không cung cấp cùng một ngữ nghĩa op, hãy ưu tiên sử dụng custom_call.
Trường version (mặc định là 0) dùng để biểu thị thời điểm ngữ nghĩa của một thành phần thay đổi.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại |
|---|---|---|
| (I1) | inputs |
số lượng giá trị thay đổi |
| (I2) | name |
hằng số thuộc loại string |
| (I3) | composite_attributes |
từ điển thuộc tính |
| (I4) | decomposition |
hằng số thuộc loại string |
| (I5) | version |
hằng số thuộc loại si32 |
Kết quả đầu ra
| Tên | Loại |
|---|---|
results |
số lượng giá trị thay đổi |
Giới hạn
- (C1)
is_namespaced_op_name(name) - (C2)
is_defined_in_parent_scope(decomposition) - (C3)
types(inputs...) == input_types(decomposition) - (C4)
types(results...) == output_types(decomposition)
Ví dụ
%results = "stablehlo.composite"(%input0, %input1) {
name = "my_namespace.my_op",
composite_attributes = {
my_attribute = "my_value"
},
decomposition = @my_op,
version = 1 : i32
} : (tensor<f32>, tensor<f32>) -> tensor<f32>
nối
Ngữ nghĩa
Nối inputs dọc theo phương diện dimension theo cùng một thứ tự như các đối số đã cho và tạo ra một tenxơ result. Một cách chính thức hơn, result[i0, ..., id, ..., iR-1] = inputs[k][i0, ..., kd, ..., iR-1], trong đó:
id = d0 + ... + dk-1 + kd.dbằngdimensionvàd0, ... là kích thước thứ nguyên thứdcủainputs.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | inputs |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C6) |
| (I2) | dimension |
hằng số thuộc loại si64 |
(C2), (C4), (C6) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C5-C6) |
Giới hạn
- (C1)
same(element_type(inputs...)). - (C2)
same(shape(inputs...))ngoại trừdim(inputs..., dimension). - (C3)
0 < size(inputs). - (C4)
0 <= dimension < rank(inputs[0]). - (C5)
element_type(result) = element_type(inputs[0]). - (C6)
shape(result) = shape(inputs[0])ngoại trừ:dim(result, dimension) = dim(inputs[0], dimension) + ....
Ví dụ
// %input0: [[1, 2], [3, 4], [5, 6]]
// %input1: [[7, 8]]
%result = "stablehlo.concatenate"(%input0, %input1) {
dimension = 0 : i64
} : (tensor<3x2xi64>, tensor<1x2xi64>) -> tensor<4x2xi64>
// %result: [[1, 2], [3, 4], [5, 6], [7, 8]]
hằng số
Ngữ nghĩa
Tạo một tensor output từ một hằng số value.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | value |
hằng số | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
output |
tensor hoặc tensor được lượng tử hoá | (C1) |
Giới hạn
- (C1)
type(value) = type(output).
Ví dụ
%output = "stablehlo.constant"() {
value = dense<[[0.0, 1.0], [2.0, 3.0]]> : tensor<2x2xf32>
} : () -> tensor<2x2xf32>
// %output: [[0.0, 1.0], [2.0, 3.0]]
chuyển đổi
Ngữ nghĩa
Thực hiện quá trình chuyển đổi từng phần tử từ một loại phần tử sang một loại phần tử khác trên tensor operand và tạo ra một tensor result.
Đối với các lượt chuyển đổi boolean-to-any-supported-type, giá trị false được chuyển đổi thành 0 và giá trị true được chuyển đổi thành 1. Đối với các lượt chuyển đổi any-supported-type-to-boolean, giá trị 0 sẽ được chuyển đổi thành false và các giá trị khác 0 sẽ được chuyển đổi thành true. Hãy xem bên dưới để biết cách hoạt động này đối với các loại phức tạp.
Đối với các lượt chuyển đổi liên quan đến số nguyên sang số nguyên, số nguyên sang dấu phẩy động hoặc dấu phẩy động sang dấu phẩy động, nếu giá trị nguồn có thể được biểu thị chính xác trong loại đích, thì giá trị kết quả sẽ là giá trị biểu thị chính xác đó. Nếu không, hành vi này sẽ được xác định sau (#180).
Đối với các lượt chuyển đổi liên quan đến floating-point-to-integer, phần thập phân sẽ bị cắt bớt. Nếu giá trị bị cắt không thể được biểu thị trong loại đích đến, thì hành vi sẽ là TBD (#180).
Quá trình chuyển đổi liên quan đến số phức sang số phức tuân theo cùng một hành vi của quá trình chuyển đổi số thực sang số thực để chuyển đổi các phần thực và phần ảo.
Đối với các lượt chuyển đổi complex-to-any-other-type và any-other-type-to-complex, giá trị ảo nguồn sẽ bị bỏ qua hoặc giá trị ảo đích sẽ được đặt về 0, tương ứng. Việc chuyển đổi phần thực tuân theo các quy tắc chuyển đổi số thực dấu phẩy động.
Về nguyên tắc, thao tác này có thể biểu thị việc khử lượng tử hoá (chuyển đổi từ các tensor được lượng tử hoá thành các tensor thông thường), lượng tử hoá (chuyển đổi từ các tensor thông thường thành các tensor được lượng tử hoá) và lượng tử hoá lại (chuyển đổi giữa các tensor được lượng tử hoá), nhưng hiện tại chúng ta có các thao tác chuyên dụng cho việc đó – uniform_dequantize cho trường hợp sử dụng đầu tiên và uniform_quantize cho trường hợp sử dụng thứ hai và thứ ba. Trong tương lai, hai thao tác này có thể được hợp nhất thành convert (#1576).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor | (C1) |
Giới hạn
- (C1)
shape(operand) = shape(result).
Ví dụ
// %operand: [-1, 0, 1]
%result = "stablehlo.convert"(%operand) : (tensor<3xi64>) -> tensor<3xcomplex<f64>>
// %result: [(-1.0, 0.0), (0.0, 0.0), (1.0, 0.0)]
tích chập
Ngữ nghĩa
Tính tích vô hướng giữa các cửa sổ của lhs và các lát của rhs, đồng thời tạo ra result. Sơ đồ sau đây cho thấy cách tính toán các phần tử trong result từ lhs và rhs bằng một ví dụ cụ thể.
Cụ thể hơn, hãy xem xét việc điều chỉnh lại các đầu vào sau đây theo lhs để có thể biểu thị các cửa sổ lhs:
lhs_window_dimensions = lhs_shape(dim(lhs, input_batch_dimension), dim(rhs, kernel_spatial_dimensions), dim(lhs, input_feature_dimension)).lhs_window_strides = lhs_shape(1, window_strides, 1).lhs_padding = lhs_shape([0, 0], padding, [0, 0]).lhs_base_dilations = lhs_shape(1, lhs_dilation, 1).lhs_window_dilations = lhs_shape(1, rhs_dilation, 1).
Việc điều chỉnh khung hình này sử dụng các hàm trợ giúp sau:
lhs_shape(n, hw, c) = permute([n] + hw + [c], [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]).result_shape(n1, hw, c1) = permute([n1] + hw + [c1], [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]).permute([j0, j1, ..., jR-1], permutation) = [i0, i1, ..., iR-1]nơij[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 dùng, nên trong tương lai, chúng tôi dự định sẽ xoá tính năng này (#1181).dot_product = dot_general(reversed_lhs_window, rhs, lhs_batching_dimensions=[], lhs_contracting_dimensions=input_spatial_dimensions + [input_feature_dimension], rhs_batching_dimensions=[], rhs_contracting_dimensions=kernel_spatial_dimensions + [kernel_input_feature_dimension]).
Nếu feature_group_count > 1:
lhses = split(lhs, feature_group_count, input_feature_dimension).rhses = split(rhs, feature_group_count, kernel_output_feature_dimension).results... = convolution(lhses..., rhses..., ..., feature_group_count=1, ...).result = concatenate(results, output_feature_dimension).
Nếu batch_group_count > 1:
lhses = split(lhs, batch_group_count, input_batch_dimension).rhses = split(rhs, batch_group_count, kernel_output_feature_dimension).results... = convolution(lhses..., rhses..., ..., batch_group_count=1, ...).result = concatenate(results, output_feature_dimension).
Đối với các loại được lượng tử hoá, hãy thực hiện dequantize_op_quantize(
lambda lhs, rhs: convolution(lhs, rhs, window_strides, padding,
lhs_dilation, rhs_dilation, window_reversal, input_batch_dimension,
input_feature_dimension, input_spatial_dimensions,
kernel_input_feature_dimension, kernel_output_feature_dimension,
kernel_spatial_dimensions, output_batch_dimension,
output_feature_dimension, output_spatial_dimensions,
feature_group_count, batch_group_count, precision_config), lhs, rhs,
type(result)).
Đối với các loại được lượng tử hoá kết hợp, hãy thực hiện hybrid_dequantize_then_op(
lambda lhs, rhs: convolution(lhs, rhs, window_strides, padding,
lhs_dilation, rhs_dilation, window_reversal, input_batch_dimension,
input_feature_dimension, input_spatial_dimensions,
kernel_input_feature_dimension, kernel_output_feature_dimension,
kernel_spatial_dimensions, output_batch_dimension,
output_feature_dimension, output_spatial_dimensions,
feature_group_count, batch_group_count, precision_config), lhs, rhs).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1), (C10-C11), (C14) (C25), (C27-C28), (C31-C32), (C34) |
| (I2) | rhs |
tensor hoặc tensor được lượng tử hoá | (C1), (C14-C16), (C25), (C27-C29), (C31-C34) |
| (I3) | window_strides |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2-C3), (C25) |
| (I4) | padding |
Hằng số tensor 2 chiều thuộc loại si64 |
(C4), (C25) |
| (I5) | lhs_dilation |
Hằng số tensor 1 chiều thuộc loại si64 |
(C5-C6), (C25) |
| (I6) | rhs_dilation |
Hằng số tensor 1 chiều thuộc loại si64 |
(C7 – C8), (C25) |
| (I7) | window_reversal |
Hằng số tensor 1 chiều thuộc loại i1 |
(C9) |
| (I8) | input_batch_dimension |
hằng số thuộc loại si64 |
(C10), (C13), (C25) |
| (I9) | input_feature_dimension |
hằng số thuộc loại si64 |
(C11), (C13-C14) |
| (I10) | input_spatial_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C12), (C13), (C25) |
| (I11) | kernel_input_feature_dimension |
hằng số thuộc loại si64 |
(C14), (C18) |
| (I12) | kernel_output_feature_dimension |
hằng số thuộc loại si64 |
(C15-C16), (C18), (C25), (C29) |
| (I13) | kernel_spatial_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C17-C18), (C25) |
| (I14) | output_batch_dimension |
hằng số thuộc loại si64 |
(C20), (C25) |
| (I15) | output_feature_dimension |
hằng số thuộc loại si64 |
(C20), (C25), (C30) |
| (I16) | output_spatial_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C19-C20), (C25) |
| (I17) | feature_group_count |
hằng số thuộc loại si64 |
(C11), (C14), (C16), (C21), (C23) |
| (I18) | batch_group_count |
hằng số thuộc loại si64 |
(C10), (C15), (C22), (C23), (C25) |
| (I19) | precision_config |
số lượng enum có thể thay đổi của DEFAULT, HIGH và HIGHEST |
(C24) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá | (C25-C28), (C30), (C32-34) |
Giới hạn
- (C1)
N = rank(lhs) = rank(rhs). - (C2)
size(window_strides) = N - 2. - (C3)
0 < window_strides. - (C4)
shape(padding) = [N - 2, 2]. - (C5)
size(lhs_dilation) = N - 2. - (C6)
0 < lhs_dilation. - (C7)
size(rhs_dilation) = N - 2. - (C8)
0 < rhs_dilation. - (C9)
size(window_reversal) = N - 2. - (C10)
dim(lhs, input_batch_dimension) % batch_group_count = 0. - (C11)
dim(lhs, input_feature_dimension) % feature_group_count = 0. - (C12)
size(input_spatial_dimensions) = N - 2. - (C13) Cho
input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]:is_unique(input_dimensions).0 <= input_dimensions < N.
- (C14)
dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count. - (C15)
dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0. - (C16)
dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0. - (C17)
size(kernel_spatial_dimensions) = N - 2. - (C18) Điều kiện
kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]:is_unique(kernel_dimensions).0 <= kernel_dimensions < N.
- (C19)
size(output_spatial_dimensions) = N - 2. - (C20) Cho
output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]:is_unique(output_dimensions).0 <= output_dimensions < N.
- (C21)
0 < feature_group_count. - (C22)
0 < batch_group_count. - (C23)
feature_group_count = 1 or batch_group_count = 1. - (C24)
size(precision_config) = 2. - (C25)
dim(result, result_dim)được xác định là:dim(lhs, input_batch_dimension) / batch_group_countnếuresult_dim = output_batch_dimension.dim(rhs, kernel_output_feature_dimension)nếuresult_dim = output_feature_dimension.num_windowsnếu không, trong đó:output_spatial_dimensions[spatial_dim] = result_dim.lhs_dim = input_spatial_dimensions[spatial_dim].rhs_dim = kernel_spatial_dimensions[spatial_dim].dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1.padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1].dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1.is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim].num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1.
- (C26)
rank(result) = N. - Nếu thao tác sử dụng các tensor không được định lượng:
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result).
- (C27)
- Nếu thao tác sử dụng các tensor được định lượng:
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs). - (C29) Nếu
is_per_axis_quantized(rhs), thìquantization_dimension(rhs) = kernel_output_feature_dimension. - (C30) Nếu
is_per_axis_quantized(result), thìquantization_dimension(result) = output_feature_dimension. - Nếu
is_quantized(lhs): - (C31)
storage_type(lhs) = storage_type(rhs). - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result). - (C33) Nếu
is_per_tensor_quantized(rhs), thìis_per_tensor_quantized(result). - Nếu
!is_quantized(lhs): - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result).
- (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 trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
costừ IEEE-754. - Đối với số phức: cosin phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(cosine, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.cosine"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[1.0, 0.0], [-1.0, 0.0]]
count_leading_zeros
Ngữ nghĩa
Thực hiện việc đếm theo phần tử số lượng bit 0 ở đầu trong tenxơ operand và tạo ra một tenxơ result.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại số nguyên | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên | (C1) |
Giới hạn
- (C1)
type(operand) = type(result).
Ví dụ
// %operand: [[0, 1], [128, -1]]
%result = "stablehlo.count_leading_zeros"(%operand) : (tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[64, 63], [56, 0]]
custom_call
Ngữ nghĩa
Đóng gói một thao tác do quá trình triển khai xác định call_target_name nhận inputs và called_computations, đồng thời tạo ra results. has_side_effect, backend_config và api_version có thể được dùng để cung cấp siêu dữ liệu bổ sung do quá trình triển khai xác định.
Hiện tại, thao tác này chứa một tập hợp siêu dữ liệu khá lộn xộn, phản ánh sự phát triển tự nhiên của thao tác tương ứng trong trình biên dịch XLA. Trong tương lai, chúng tôi dự định sẽ hợp nhất siêu dữ liệu này (#741).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại |
|---|---|---|
| (I1) | inputs |
số lượng giá trị thay đổi |
| (I2) | call_target_name |
hằng số thuộc loại string |
| (I3) | has_side_effect |
hằng số thuộc loại i1 |
| (I4) | backend_config |
hằng số thuộc loại string hoặc từ điển thuộc tính |
| (I5) | api_version |
hằng số thuộc loại si32 |
| (I6) | called_computations |
số lượng hằng số có thể thay đổi thuộc loại string |
| (I7) | output_operand_aliases |
chỉ định các phần bí danh trong đầu ra và toán hạng |
Kết quả đầu ra
| Tên | Loại |
|---|---|
results |
số lượng giá trị thay đổi |
(Hỗ trợ GPU XLA) Các mục tiêu custom_call đặc biệt
Có 3 call_target_name đặc biệt liên quan đến các loại buffer: CreateBuffer tạo một buffer chưa được khởi chạy, Pin tạo một buffer đã được khởi chạy và Unpin giải phóng một buffer và trả về nội dung của buffer.
%uninitialized_buffer = "stablehlo.custom_call"() {
call_target_name = "CreateBuffer",
api_version = 4 : i32,
} : () -> memref<4xf64>
%initialized_buffer = "stablehlo.custom_call"(%init_value) {
call_target_name = "Pin",
api_version = 4 : i32,
} : (tensor<4xf64>) -> memref<4xf64>
%dealloc_buffer = "stablehlo.custom_call"(%initialized_buffer) {
call_target_name = "Unpin",
api_version = 4 : i32,
} : (memref<4xf64>) -> tensor<4xf64>
Email đại diện
Một số hoạt động custom_call có thể yêu cầu một phần trong đầu ra và một phần trong các toán hạng để dùng chung bộ nhớ. Bạn có thể biểu thị điều này qua output_operand_aliases. Biểu diễn cặp bí danh bao gồm một danh sách các chỉ mục bộ dữ liệu đầu ra biểu thị phần đầu ra và một operand_index cùng với một danh sách các chỉ mục bộ dữ liệu toán hạng biểu thị phần toán hạng. Danh sách các chỉ mục bộ dữ liệu đầu ra hoặc toán hạng sẽ trống nếu loại tương ứng không phải là loại tuple và có thể có độ dài tuỳ ý đối với loại bộ dữ liệu lồng nhau tuỳ ý. Điều này tương tự như phần biểu thị biệt hiệu XLA.
Phần đầu ra và phần đầu vào trong một cặp bí danh phải có cùng loại. Đối với các hoạt động custom_call không phải là lệnh gọi đến CreateBuffer, Pin và Unpin, toán hạng buffer có thể xuất hiện tối đa trong một cặp bí danh và đầu ra buffer phải xuất hiện trong một cặp bí danh.
Ví dụ
%results = "stablehlo.custom_call"(%input0) {
call_target_name = "foo",
has_side_effect = false,
backend_config = {bar = 42 : i32},
api_version = 4 : i32,
called_computations = [@foo]
} : (tensor<f64>) -> tensor<f64>
%updated_buffer = "stablehlo.custom_call"(%buffer) {
call_target_name = "Update",
api_version = 4 : i32,
output_operand_aliases = [
#stablehlo.output_operand_alias<output_tuple_indices = [],
operand_index = 0,
operand_tuple_indices = []>]
} : (memref<4xf64>) -> memref<4xf64>
chia
Ngữ nghĩa
Thực hiện phép chia từng phần tử của các tensor số bị chia lhs và số chia rhs, đồng thời tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số nguyên: phép chia số nguyên tạo ra thương số đại số mà mọi phần phân số đều bị loại bỏ.
- Đối với số thực:
divisiontừ IEEE-754. - Đối với số phức: phép chia số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(divide, lhs, rhs, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
| (I2) | rhs |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).
Ví dụ
// %lhs: [17.1, -17.1, 17.1, -17.1]
// %rhs: [3.0, 3.0, -3.0, -3.0]
%result = "stablehlo.divide"(%lhs, %rhs) : (tensor<4xf32>, tensor<4xf32>) -> tensor<4xf32>
// %result: [5.66666651, -5.66666651, -5.66666651, 5.66666651]
dot_general
Ngữ nghĩa
Tính tích vô hướng giữa các lát của lhs và các lát của rhs, đồng thời tạo ra một tenxơ result.
Cụ thể hơn, result[result_index] = dot_product, trong đó:
lhs_result_dimensions = [d for d in axes(lhs) and d not in lhs_batching_dimensions and d not in lhs_contracting_dimensions].rhs_result_dimensions = [d for d in axes(rhs) and d not in rhs_batching_dimensions and d not in rhs_contracting_dimensions].result_batching_index + result_lhs_index + result_rhs_index = result_indextrong đósize(result_batching_index) = size(lhs_batching_dimensions),size(result_lhs_index) = size(lhs_result_dimensions)vàsize(result_rhs_index) = size(rhs_result_dimensions).transposed_lhs = transpose(lhs, lhs_batching_dimensions + lhs_result_dimensions + lhs_contracting_dimensions).transposed_lhs_slice = slice(transposed_lhs, result_batching_index + result_lhs_index + [:, ..., :]).reshaped_lhs_slice = reshape(transposed_lhs_slice, dims(lhs, lhs_contracting_dimensions)).transposed_rhs = transpose(rhs, rhs_batching_dimensions + rhs_result_dimensions + rhs_contracting_dimensions).transposed_rhs_slice = slice(transposed_rhs, result_batching_index + result_rhs_index + [:, ..., :]).reshaped_rhs_slice = reshape(transposed_rhs_slice, dims(rhs, rhs_contracting_dimensions)).dot_product = reduce( inputs=[multiply(reshaped_lhs_slice, reshaped_rhs_slice)], init_values=[constant(0, element_type(result))], dimensions=range(size(lhs_contracting_dimensions)), body=lambda x, y: add(x, y)).
Đối với các loại được lượng tử hoá, hãy thực hiện dequantize_op_quantize(
lambda lhs, rhs: dot_general(lhs, rhs, lhs_batching_dimensions,
rhs_batching_dimensions, lhs_contracting_dimensions,
rhs_contracting_dimensions, precision_config), lhs, rhs, type(result)).
Đối với các loại được lượng tử hoá kết hợp, hãy thực hiện hybrid_dequantize_then_op(
lambda lhs, rhs: dot_general(lhs, rhs, lhs_batching_dimensions,
rhs_batching_dimensions, lhs_contracting_dimensions,
rhs_contracting_dimensions, precision_config), lhs, rhs).
precision_config kiểm soát sự đánh đổi giữa tốc độ và độ chính xác cho các phép tính trên các phần phụ trợ của bộ tăng tốc. Đây có thể là một trong những giá trị sau (hiện tại, ngữ nghĩa của các giá trị enum này chưa được chỉ định rõ ràng, nhưng chúng tôi dự định giải quyết vấn đề này trong #755):
DEFAULT: Tính toán nhanh nhất, nhưng mức độ xấp xỉ chính xác nhất so với số ban đầu.HIGH: Tính toán chậm hơn, nhưng có độ chính xác cao hơn khi xấp xỉ với số ban đầu.HIGHEST: Tính toán chậm nhất, nhưng xấp xỉ chính xác nhất với số ban đầu.
DotAlgorithm xác định các thuộc tính chính của thuật toán dùng để triển khai phép toán dot, đồng thời xác định độ chính xác. Nếu bạn đặt các trường thuộc tính thuật toán, thì precision_config phải là DEFAULT. DotAlgorithms không có giá trị mặc định, vì các thông số mặc định được xác định theo cách triển khai. Do đó, bạn có thể đặt tất cả các trường thuật toán dấu chấm thành None để chỉ định một thuật toán dấu chấm trống. Thay vào đó, thuật toán này sẽ sử dụng giá trị precision_config.
Các trường DotAlgorithm bao gồm:
lhs_precision_typevàrhs_precision_type, độ chính xác mà LHS và RHS của thao tác được làm tròn. Các loại độ chính xác độc lập với các loại bộ nhớ của dữ liệu đầu vào và đầu ra.accumulation_typeđộ chính xác dùng để tích luỹ.lhs_component_count,rhs_component_countvànum_primitive_operationsáp dụng khi chúng ta đang thực hiện một thuật toán phân tách LHS và/hoặc RHS thành nhiều thành phần và thực hiện nhiều thao tác "nguyên thuỷ" trên các giá trị đó – thường là để mô phỏng độ chính xác cao hơn (ví dụ: Khai thác kiểu dữ liệu Trí tuệ nhân tạo bfloat16 để tính toán có độ chính xác cao hơn: bf16_6x tf32_3x, v.v.). Đối với các thuật toán không có quá trình phân tách, bạn nên đặt các giá trị này thành1.allow_imprecise_accumulationđể chỉ định xem có được phép tích luỹ với độ chính xác thấp hơn cho một số bước hay không (ví dụ:CUBLASLT_MATMUL_DESC_FAST_ACCUM).
Ví dụ về các thuộc tính DotAlgorithm:
// Inputs are casted to tf32, and then accumulated in f32:
{lhs_precision_type = tf32,
rhs_precision_type = tf32,
accumulation_type = f32,
lhs_component_count = 1,
rhs_component_count = 1,
num_primitive_operations = 1,
allow_imprecise_accumulation = false}
// bf16_6x: each input is decomposed to 3 bf16 components, then 6 dot operations are done on those components, and the result is accumulated in f32.
{lhs_precision_type = bf16,
rhs_precision_type = bf16,
accumulation_type = f32,
lhs_component_count = 3,
rhs_component_count = 3,
num_primitive_operations = 6,
allow_imprecise_accumulation = false}
// Inputs are (casted to) f8e5m2, and we accumulate in f32, but for some steps we may accumulate in lower precision.
{lhs_precision_type = f8e5m2,
rhs_precision_type = f8e5m2,
accumulation_type = f32,
lhs_component_count = 1,
rhs_component_count = 1,
num_primitive_operations = 1,
allow_imprecise_accumulation = true}
Các phương thức triển khai sẽ quyết định những tổ hợp được hỗ trợ. Nói chung, người dùng StableHLO không đảm bảo rằng mỗi thuật toán đều được hỗ trợ trên từng loại trình tăng tốc. Nếu một thuật toán nhất định không được hỗ trợ, thì phải phát sinh lỗi thay vì quay lại một thuật toán thay thế. Quy trình xác minh StableHLO sẽ cung cấp quy trình xác minh nỗ lực tối đa, ngăn chặn các thuật toán không được hỗ trợ trên bất kỳ phần cứng nào.
Hãy xem xla_data.proto > Algorithm để biết một số giá trị thuật toán được hỗ trợ. Phiếu yêu cầu #2483 ghi lại kế hoạch tạo một tài liệu tập trung về các thuật toán được hỗ trợ theo phần phụ trợ.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C5-C6), (C9-C10), (C12-C14), (C17-C18), (C20) |
| (I2) | rhs |
tensor hoặc tensor được lượng tử hoá | (C7-C10), (C12-C20) |
| (I3) | lhs_batching_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C3), (C5), (C9), (C12) |
| (I4) | rhs_batching_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C4), (C7), (C9) |
| (I5) | lhs_contracting_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C3), (C6), (C10) |
| (I6) | rhs_contracting_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C4), (C8), (C10), (C16) |
| (I7) | precision_config |
số lượng enum có thể thay đổi của DEFAULT, 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 được lượng tử hoá | (C12), (C14), (C18-C20) |
Giới hạn
- (C1)
size(lhs_batching_dimensions) = size(rhs_batching_dimensions). - (C2)
size(lhs_contracting_dimensions) = size(rhs_contracting_dimensions). - (C3)
is_unique(lhs_batching_dimensions + lhs_contracting_dimensions). - (C4)
is_unique(rhs_batching_dimensions + rhs_contracting_dimensions). - (C5)
0 <= lhs_batching_dimensions < rank(lhs). - (C6)
0 <= lhs_contracting_dimensions < rank(lhs). - (C7)
0 <= rhs_batching_dimensions < rank(rhs). - (C8)
0 <= rhs_contracting_dimensions < rank(rhs). - (C9)
dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...). - (C10)
dim(lhs, lhs_contracting_dimensions...) = dim(rhs, rhs_contracting_dimensions...). - (C11)
size(precision_config) = 2. - (C12)
shape(result) = dim(lhs, lhs_batching_dimensions) + dim(lhs, lhs_result_dimensions) + dim(rhs, rhs_result_dimensions). - Nếu thao tác sử dụng các tensor không được định lượng:
- (C13)
element_type(lhs) = element_type(rhs).
- (C13)
- Nếu thao tác sử dụng các tensor được định lượng:
- (C14)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs). - (C15)
zero_points(rhs) = 0. - (C16) Nếu
is_per_axis_quantized(rhs), thìquantization_dimension(rhs)không nằm 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
!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
Thao tác này có chức năng giống hệt với thao tác broadcast_in_dim, nhưng hình dạng kết quả được chỉ định linh động thông qua output_dimensions.
Thao tác này cũng chấp nhận các thuộc tính không bắt buộc known_expanding_dimensions, known_nonexpanding_dimensions để thể hiện kiến thức tĩnh về hành vi mở rộng của các phương diện.
Nếu bạn không chỉ định, thì tất cả các phương diện đều được giả định là có thể mở rộng.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor được lượng tử hoá | (C1-C2), (C5-C6), (C9) |
| (I2) | output_dimensions |
Tensor 1 chiều thuộc loại số nguyên | (C7) |
| (I3) | broadcast_dimensions |
Tenxơ hằng số 1 chiều thuộc loại số nguyên | (C2-C6) |
| (I4) | known_expanding_dimensions |
Tenxơ hằng số 1 chiều thuộc loại số nguyên | (C8-C9) |
| (I5) | known_nonexpanding_dimensions |
Tenxơ hằng số 1 chiều thuộc loại số nguyên | (C8-C9) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá | (C1), (C3), (C5-C7) |
Giới hạn
- (C1)
element_type(result)được cho bởi:element_type(operand), nếu!is_per_axis_quantized(operand).element_type(operand)ngoại trừquantization_dimension(operand),scales(operand)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ởaxes(operand):dim(operand, d) = 1hoặcdim(operand, d) = dim(result, broadcast_dimensions[d]).
- (C6) Nếu
is_per_axis_quantized(result):quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)].- Nếu
dim(operand, quantization_dimension(operand)) = 1, thìscales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result))).
- (C7)
size(output_dimensions) = rank(result). - (C8)
is_unique(known_expanding_dimensions + known_nonexpanding_dimensions). - (C9)
0 <= known_expanding_dimensions < rank(operand). - (C10)
0 <= known_nonexpanding_dimensions < rank(operand).
Ví dụ
// %operand: [
// [1, 2, 3]
// ]
%operand = stablehlo.constant dense<[[1, 2, 3]]> : tensor<1x3xi64>
%output_dimensions = stablehlo.constant dense<[2, 3, 2]> : tensor<3xi64>
%result = "stablehlo.dynamic_broadcast_in_dim"(%operand, %output_dimensions) {
broadcast_dimensions = array<i64: 2, 1>,
known_expanding_dimensions = array<i64: 0>,
known_nonexpanding_dimensions = array<i64: 1>
} : (tensor<1x3xi64>, tensor<3xi64>) -> tensor<2x3x2xi64>
// %result: [
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ],
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ]
// ]
dynamic_conv
Ngữ nghĩa
Thao tác này có chức năng giống hệt với thao tác tích chập, nhưng khoảng đệm được chỉ định một cách linh hoạt thông qua padding.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1), (C10-C11), (C14) (C25), (C26-C27), (C30-C31), (C33) |
| (I2) | rhs |
tensor hoặc tensor được lượng tử hoá | (C1), (C14-C16), (C26-C28), (C30-C33) |
| (I3) | padding |
Tensor 2 chiều thuộc loại số nguyên | (C4) |
| (I4) | window_strides |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2-C3) |
| (I5) | lhs_dilation |
Hằng số tensor 1 chiều thuộc loại si64 |
(C5-C6) |
| (I6) | rhs_dilation |
Hằng số tensor 1 chiều thuộc loại si64 |
(C7 – C8) |
| (I7) | window_reversal |
Hằng số tensor 1 chiều thuộc loại i1 |
(C9) |
| (I8) | input_batch_dimension |
hằng số thuộc loại si64 |
(C10), (C13) |
| (I9) | input_feature_dimension |
hằng số thuộc loại si64 |
(C11), (C13-C14) |
| (I10) | input_spatial_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C12), (C13) |
| (I11) | kernel_input_feature_dimension |
hằng số thuộc loại si64 |
(C14), (C18) |
| (I12) | kernel_output_feature_dimension |
hằng số thuộc loại si64 |
(C15-C16), (C18), (C28) |
| (I13) | kernel_spatial_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C17 – C18) |
| (I14) | output_batch_dimension |
hằng số thuộc loại si64 |
(C20) |
| (I15) | output_feature_dimension |
hằng số thuộc loại si64 |
(C20), (C29) |
| (I16) | output_spatial_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C19 – C20) |
| (I17) | feature_group_count |
hằng số thuộc loại si64 |
(C11), (C14), (C16), (C21), (C23) |
| (I18) | batch_group_count |
hằng số thuộc loại si64 |
(C10), (C15), (C22), (C23) |
| (I19) | precision_config |
số lượng enum có thể thay đổi của DEFAULT, HIGH và HIGHEST |
(C24) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá | (C25 – C27), (C29), (C31 – C33) |
Giới hạn
- (C1)
N = rank(lhs) = rank(rhs). - (C2)
size(window_strides) = N - 2. - (C3)
0 < window_strides. - (C4)
shape(padding) = [N - 2, 2]. - (C5)
size(lhs_dilation) = N - 2. - (C6)
0 < lhs_dilation. - (C7)
size(rhs_dilation) = N - 2. - (C8)
0 < rhs_dilation. - (C9)
size(window_reversal) = N - 2. - (C10)
dim(lhs, input_batch_dimension) % batch_group_count = 0. - (C11)
dim(lhs, input_feature_dimension) % feature_group_count = 0. - (C12)
size(input_spatial_dimensions) = N - 2. - (C13) Cho
input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]:is_unique(input_dimensions).0 <= input_dimensions < N.
- (C14)
dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count. - (C15)
dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0. - (C16)
dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0. - (C17)
size(kernel_spatial_dimensions) = N - 2. - (C18) Điều kiện
kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]:is_unique(kernel_dimensions).0 <= kernel_dimensions < N.
- (C19)
size(output_spatial_dimensions) = N - 2. - (C20) Cho
output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]:is_unique(output_dimensions).0 <= output_dimensions < N.
- (C21)
0 < feature_group_count. - (C22)
0 < batch_group_count. - (C23)
feature_group_count = 1 or batch_group_count = 1. - (C24)
size(precision_config) = 2. - (C25)
dim(result, result_dim)được xác định là:dim(lhs, input_batch_dimension) / batch_group_countnếuresult_dim = output_batch_dimension.dim(rhs, kernel_output_feature_dimension)nếuresult_dim = output_feature_dimension.num_windowsnếu không, trong đó:output_spatial_dimensions[spatial_dim] = result_dim.lhs_dim = input_spatial_dimensions[spatial_dim].rhs_dim = kernel_spatial_dimensions[spatial_dim].dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1.padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1].dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1.is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim].num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1.
- (C26)
rank(result) = N. - Nếu thao tác sử dụng các tensor không được định lượng:
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result).
- (C27)
- Nếu thao tác sử dụng các tensor được định lượng:
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs). - (C29) Nếu
is_per_axis_quantized(rhs), thìquantization_dimension(rhs) = kernel_output_feature_dimension. - (C30) Nếu
is_per_axis_quantized(result), thìquantization_dimension(result) = output_feature_dimension. - Nếu
is_quantized(lhs): - (C31)
storage_type(lhs) = storage_type(rhs). - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result). - (C33) Nếu
is_per_tensor_quantized(rhs), thìis_per_tensor_quantized(result). - Nếu
!is_quantized(lhs): - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result).
- (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
Thao tác này có chức năng giống hệt như thao tác gather, trong đó slice_sizes được chỉ định một cách linh động dưới dạng một giá trị.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C7), (C10-C12), (C14) |
| (I2) | start_indices |
tensor thuộc loại số nguyên | (C2), (C3), (C13) |
| (I3) | slice_sizes |
Tensor 1 chiều thuộc loại số nguyên | (C8), (C11-C13) |
| (I4) | offset_dims |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C4-C5), (C13) |
| (I5) | collapsed_slice_dims |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C6-C8), (C13) |
| (I6) | start_index_map |
Hằng số tensor 1 chiều thuộc loại si64 |
(C3), (C9), (C10) |
| (I7) | index_vector_dim |
hằng số thuộc loại si64 |
(C2), (C3), (C13) |
| (I8) | indices_are_sorted |
hằng số thuộc loại i1 |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C5), (C13-C14) |
Giới hạn
- (C1)
rank(operand) = size(offset_dims) + size(collapsed_slice_dims). - (C2)
0 <= index_vector_dim <= rank(start_indices). - (C3)
size(start_index_map) = index_vector_dim < rank(start_indices) ? dim(start_indices, index_vector_dim) : 1. - (C4)
is_unique(offset_dims) and is_sorted(offset_dims). - (C5)
0 <= offset_dims < rank(result). - (C6)
is_unique(collapsed_slice_dims) and is_sorted(collapsed_slice_dims). - (C7)
0 <= collapsed_slice_dims < rank(operand). - (C8)
slice_sizes[collapsed_slice_dims...] <= 1. - (C9)
is_unique(start_index_map). - (C10)
0 <= start_index_map < rank(operand). - (C11)
size(slice_sizes) = rank(operand). - (C12)
0 <= slice_sizes <= shape(operand). - (C13)
shape(result) = combine(batch_dim_sizes, offset_dim_sizes)trong đó:batch_dim_sizes = shape(start_indices), ngoại trừ kích thước phương diệnstart_indicestương ứng vớiindex_vector_dimkhông được đưa vào.offset_dim_sizes = shape(slice_sizes), ngoại trừ kích thước phương diện trongslice_sizestương ứng vớicollapsed_slice_dimskhông được đưa vào.combineđặtbatch_dim_sizestại các trục tương ứng vớibatch_dimsvàoffset_dim_sizestại các trục tương ứng vớioffset_dims.
- (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
Thao tác này có chức năng giống hệt với iota op, nhưng hình dạng kết quả được chỉ định linh động thông qua output_shape.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | output_shape |
Tensor 1 chiều thuộc loại số nguyên | (C1), (C2) |
| (I2) | iota_dimension |
si64 |
(C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C2) |
Giới hạn
- (C1)
0 <= iota_dimension < size(output_shape). - (C2)
rank(result) = size(output_shape).
Ví dụ
%output_shape = stablehlo.constant dense<[4, 5]> : tensor<2xi64>
%result = "stablehlo.dynamic_iota"(%output_shape) {
iota_dimension = 0 : i64
} : (tensor<2xi64>) -> tensor<4x5xi64>
// %result: [
// [0, 0, 0, 0, 0],
// [1, 1, 1, 1, 1],
// [2, 2, 2, 2, 2],
// [3, 3, 3, 3, 3]
// ]
dynamic_pad
Ngữ nghĩa
Thao tác này có chức năng giống hệt với thao tác pad, nhưng edge_padding_low, edge_padding_high và interior_padding được chỉ định linh động dưới dạng các giá trị.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C2), (C4) |
| (I2) | padding_value |
Tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
| (I3) | edge_padding_low |
Tensor 1 chiều thuộc loại số nguyên | (C1), (C4) |
| (I4) | edge_padding_high |
Tensor 1 chiều thuộc loại số nguyên | (C1), (C4) |
| (I5) | interior_padding |
Tensor 1 chiều thuộc loại số nguyên | (C2-C4) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C3 – C6) |
Giới hạn
- (C1)
element_type(operand) = element_type(padding_value) = element_type(result). - (C2)
size(edge_padding_low) = size(edge_padding_high) = size(interior_padding) = rank(operand). - (C3)
0 <= interior_padding. - (C4)
shape(result) = shape(operand) + edge_padding_low + max(shape(operand) - 1, 0) * interior_padding + edge_padding_high.
Ví dụ
// %operand: [
// [1, 2, 3],
// [4, 5, 6]
// ]
// %padding_value: 0
// %edge_padding_low: [0, 1]
// %edge_padding_high: [2, 1]
// %interior_padding: [1, 2]
%result = "stablehlo.dynamic_pad"(%operand, %padding_value,
%edge_padding_low, %edge_padding_high, %interior_padding
) : (tensor<2x3xi64>, tensor<i64>, tensor<2xi64>, tensor<2xi64>, tensor<2xi64>) -> tensor<5x9xi64>
// %result: [
// [0, 1, 0, 0, 2, 0, 0, 3, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0],
// [0, 4, 0, 0, 5, 0, 0, 6, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0]
// ]
dynamic_reshape
Ngữ nghĩa
Thao tác này có chức năng giống hệt với thao tác reshape, nhưng hình dạng kết quả được chỉ định linh động thông qua output_shape.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor được lượng tử hoá | (C1-C3) |
| (I2) | output_shape |
Tensor 1 chiều thuộc loại số nguyên | (C4) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá | (C1-C4) |
Giới hạn
- (C1)
element_type(result)được cho bởi:element_type(operand), nếu!is_per_axis_quantized(operand).element_type(operand), ngoại trừquantization_dimension(operand)vàquantization_dimension(result)có thể khác nhau.
- (C2)
size(operand) = size(result). - (C3) Nếu
is_per_axis_quantized(operand):reduce(dims(operand, [0, 1, ..., quantization_dimension(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [0, 1, ..., quantization_dimension(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).dim(operand, quantization_dimension(operand)) = dim(result, quantization_dimension(result)).reduce(dims(operand, [quantization_dimension(operand) + 1, ..., rank(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [quantization_dimension(result) + 1, ..., rank(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).
- (C4)
size(output_shape) = rank(result).
Ví dụ
// %operand: [[1, 2, 3], [4, 5, 6]]
// %output_shape: [3, 2]
%result = "stablehlo.dynamic_reshape"(%operand, %output_shape) : (tensor<2x3xi64>, tensor<2xi64>) -> tensor<3x2xi64>
// %result: [[1, 2], [3, 4], [5, 6]]
dynamic_slice
Ngữ nghĩa
Trích xuất một lát cắt từ operand bằng cách sử dụng các chỉ mục bắt đầu được tính toán động và tạo ra một tensor result. start_indices chứa chỉ mục bắt đầu của lát cho mỗi phương diện, có thể điều chỉnh và slice_sizes chứa kích thước của lát cho mỗi phương diện. Một cách chính thức hơn, result[result_index] = operand[operand_index] trong đó:
adjusted_start_indices = clamp(0, start_indices, shape(operand) - slice_sizes).operand_index = adjusted_start_indices + result_index.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C2), (C4) |
| (I2) | start_indices |
số lượng biến đổi của tensor 0 chiều thuộc loại số nguyên | (C2), (C3) |
| (I3) | slice_sizes |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C4), (C5) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C5) |
Giới hạn
- (C1)
element_type(operand) = element_type(result). - (C2)
size(start_indices) = size(slice_sizes) = rank(operand). - (C3)
same(type(start_indices...)). - (C4)
0 <= slice_sizes <= shape(operand). - (C5)
shape(result) = slice_sizes.
Ví dụ
// %operand: [
// [0, 0, 1, 1],
// [0, 0, 1, 1],
// [0, 0, 0, 0],
// [0, 0, 0, 0]
// ]
// %start_indices0: -1
// %start_indices1: 3
%result = "stablehlo.dynamic_slice"(%operand, %start_indices0, %start_indices1) {
slice_sizes = array<i64: 2, 2>
} : (tensor<4x4xi32>, tensor<i64>, tensor<i64>) -> tensor<2x2xi32>
// %result: [
// [1, 1],
// [1, 1]
// ]
dynamic_update_slice
Ngữ nghĩa
Tạo một tensor result bằng với tensor operand, ngoại trừ việc lát bắt đầu từ start_indices được cập nhật bằng các giá trị trong update.
Cụ thể hơn, result[result_index] được xác định là:
update[update_index]nếu0 <= update_index < shape(update)trong đó:adjusted_start_indices = clamp(0, start_indices, shape(operand) - shape(update)).update_index = result_index - adjusted_start_indices.
- Nếu không thì là
operand[result_index].
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C4), (C6) |
| (I2) | update |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C2), (C3), (C6) |
| (I3) | start_indices |
số lượng biến đổi của tensor 0 chiều thuộc loại số nguyên | (C4), (C5) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
type(operand) = type(result). - (C2)
element_type(update) = element_type(operand). - (C3)
rank(update) = rank(operand). - (C4)
size(start_indices) = rank(operand). - (C5)
same(type(start_indices...)). - (C6)
0 <= shape(update) <= shape(operand).
Ví dụ
// %operand: [
// [1, 1, 0, 0],
// [1, 1, 0, 0],
// [1, 1, 1, 1],
// [1, 1, 1, 1]
// ]
// %update: [
// [1, 1],
// [1, 1]
// ]
// %start_indices0: -1
// %start_indices1: 3
%result = "stablehlo.dynamic_update_slice"(%operand, %update, %start_indices0, %start_indices1)
: (tensor<4x4xi32>, tensor<2x2xi32>, tensor<i64>, tensor<i64>) -> tensor<4x4xi32>
// %result: [
// [1, 1, 1, 1],
// [1, 1, 1, 1],
// [1, 1, 1, 1],
// [1, 1, 1, 1]
// ]
hàm mũ
Ngữ nghĩa
Thực hiện phép toán hàm mũ trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
exptừ IEEE-754. - Đối với số phức: hàm mũ phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(exponential, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.exponential"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[1.0, 2.7182818284590451], [7.3890560989306504, 20.085536923187668]]
exponential_minus_one
Ngữ nghĩa
Thực hiện phép toán trừ 1 theo hàm mũ trên tensor operand và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
expm1từ IEEE-754. - Đối với số phức: số mũ phức trừ đi 1.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(exponential_minus_one, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [0.0, 1.0]
%result = "stablehlo.exponential_minus_one"(%operand) : (tensor<2xf64>) -> tensor<2xf64>
// %result: [0.0, 1.71828187]
fft
Ngữ nghĩa
Thực hiện phép biến đổi Fourier thuận và nghịch cho các đầu vào/đầu ra thực và phức.
fft_type là một trong những giá trị sau:
FFT: Chuyển tiếp FFT phức tạp sang phức tạp.IFFT: FFT phức tạp sang phức tạp nghịch đảo.RFFT: Chuyển tiếp FFT thực sang phức.IRFFT: FFT thực sang phức nghịch đảo (tức là lấy số phức, trả về số thực).
Cụ thể hơn, cho hàm fft nhận các tensor 1 chiều thuộc các kiểu phức tạp làm dữ liệu đầu vào, tạo ra các tensor 1 chiều thuộc cùng các kiểu làm dữ liệu đầu ra và tính toán phép biến đổi Fourier rời rạc:
Đối với fft_type = FFT, result được xác định là kết quả cuối cùng của một loạt các phép tính L, trong đó L = size(fft_length). Ví dụ: đối với L = 3:
result1[i0, ..., :] = fft(operand[i0, ..., :]).result2[i0, ..., :, iR-1] = fft(result1[i0, ..., :, iR-1]).result[i0, ..., :, iR-2, iR-1] = fft(result2[i0, ..., :, iR-2, iR-1]).
Hơn nữa, cho hàm ifft có cùng chữ ký kiểu và tính toán nghịch đảo của fft:
Đối với fft_type = IFFT, result được xác định là nghịch đảo của các phép tính cho fft_type = FFT. Ví dụ: đối với L = 3:
result1[i0, ..., :, iR-2, iR-1] = ifft(operand[i0, ..., :, iR-2, iR-1]).result2[i0, ..., :, iR-1] = ifft(result1[i0, ..., :, iR-1]).result[i0, ..., :] = ifft(result2[i0, ..., :]).
Ngoài ra, cho hàm rfft lấy tensor 1 chiều thuộc các loại dấu phẩy động, tạo ra tensor 1 chiều thuộc các loại phức tạp có cùng ngữ nghĩa dấu phẩy động và hoạt động như sau:
rfft(real_operand) = truncated_resultnơicomplex_operand... = (real_operand..., 0.0).complex_result = fft(complex_operand).truncated_result = complex_result[:(rank(complex_result) / 2 + 1)].
(Khi biến đổi Fourier rời rạc được tính cho các toán hạng thực, N/2 + 1 phần tử đầu tiên của kết quả xác định rõ ràng phần còn lại của kết quả, vì vậy kết quả của rfft sẽ bị cắt bớt để tránh tính toán các phần tử dư thừa).
Đối với fft_type = RFFT, result được xác định là kết quả cuối cùng của một loạt các phép tính L, trong đó L = size(fft_length). Ví dụ: đối với L = 3:
result1[i0, ..., :] = rfft(operand[i0, ..., :]).result2[i0, ..., :, iR-1] = fft(result1[i0, ..., :, iR-1]).result[i0, ..., :, iR-2, iR-1] = fft(result2[i0, ..., :, iR-2, iR-1]).
Cuối cùng, cho hàm irfft có cùng chữ ký kiểu và tính toán nghịch đảo của rfft:
Đối với fft_type = IRFFT, result được xác định là nghịch đảo của các phép tính cho fft_type = RFFT. Ví dụ: đối với L = 3:
result1[i0, ..., :, iR-2, iR-1] = ifft(operand[i0, ..., :, iR-2, iR-1]).result2[i0, ..., :, iR-1] = ifft(result1[i0, ..., :, iR-1]).result[i0, ..., :] = irfft(result2[i0, ..., :]).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại số phức hoặc dấu phẩy động | (C1), (C2), (C4), (C5) |
| (I2) | fft_type |
enum của FFT, IFFT, 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 thuộc loại số phức hoặc dấu phẩy động | (C2), (C4), (C5) |
Giới hạn
- (C1)
size(fft_length) <= rank(operand). - (C2) Mối quan hệ giữa các loại phần tử
operandvàresultcó sự khác biệt:- Nếu
fft_type = FFT,element_type(operand)vàelement_type(result)có cùng kiểu 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 kiểu dấu phẩy động vàelement_type(result)là một kiểu phức tạp có cùng ngữ nghĩa dấu phẩy động. - Nếu
fft_type = IRFFT,element_type(operand)là một kiểu phức tạp vàelement_type(result)là một kiểu dấu phẩy động có cùng ngữ nghĩa dấu phẩy động.
- Nếu
- (C3)
1 <= size(fft_length) <= 3. - (C4) Nếu trong số
operandvàresult, có một tensorrealthuộc loại dấu phẩy động, thìshape(real)[-size(fft_length):] = fft_length. - (C5)
shape(result) = shape(operand)ngoại trừ:- Nếu
fft_type = RFFT,dim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1. - Nếu
fft_type = IRFFT,dim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1.
- Nếu
Ví dụ
// %operand: [(1.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
%result = "stablehlo.fft"(%operand) {
fft_type = #stablehlo<fft_type FFT>,
fft_length = array<i64: 4>
} : (tensor<4xcomplex<f32>>) -> tensor<4xcomplex<f32>>
// %result: [(1.0, 0.0), (1.0, 0.0), (1.0, 0.0), (1.0, 0.0)]
tầng
Ngữ nghĩa
Thực hiện phép làm tròn xuống theo phần tử của tensor operand và tạo ra một tensor result.
Triển khai thao tác roundToIntegralTowardNegative theo quy cách IEEE-754. Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(floor, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [-0.8166, -0.2530, 0.2530, 0.8166, 2.0]
%result = "stablehlo.floor"(%operand) : (tensor<5xf32>) -> tensor<5xf32>
// %result: [-1.0, -1.0, 0.0, 0.0, 2.0]
tập hợp
Ngữ nghĩa
Thu thập các lát cắt từ tensor operand từ các độ lệch được chỉ định trong start_indices và tạo ra một tensor result.
Sơ đồ sau đây cho thấy cách các phần tử trong result liên kết với các phần tử trong operand bằng một ví dụ cụ thể. Sơ đồ này chọn một vài chỉ mục result mẫu và giải thích chi tiết chỉ mục operand mà chúng tương ứng.
Một cách chính thức hơn, result[result_index] = operand[operand_index] trong đó:
batch_dims = [d for d in axes(result) and d not in offset_dims].batch_index = result_index[batch_dims...].start_indexđược xác định là:start_indices[bi0, ..., :, ..., biN]trong đóbilà các phần tử riêng lẻ trongbatch_indexvà:được chèn vào chỉ mụcindex_vector_dim, nếuindex_vector_dim<rank(start_indices).- Nếu không thì là
[start_indices[batch_index]].
- Đối với
d_operandtrongaxes(operand),full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand])nếud_operand = start_index_map[d_start].- Nếu không thì là
full_start_index[d_operand] = 0.
- Đối với
d_operandtrongaxes(operand),full_batching_index[d_operand] = batch_index[d_start - (d_start < index_vector_dim ? 0 : 1)]nếud_operand = operand_batching_dims[i_batching]vàd_start = start_indices_batching_dims[i_batching].- Nếu không thì là
full_batching_index[d_operand] = 0.
offset_index = result_index[offset_dims...].full_offset_index = [oi0, ..., 0, ..., oiN]trong đóoilà các phần tử riêng lẻ trongoffset_indexvà0được chèn ở các chỉ mục từcollapsed_slice_dimsvàoperand_batching_dims.operand_index = full_start_index + full_batching_index + full_offset_index.
Nếu indices_are_sorted là true thì quá trình triển khai có thể giả định rằng start_indices được sắp xếp theo start_index_map, nếu không, hành vi sẽ không xác định. Cụ thể hơn, đối với tất cả i1 < i2 từ indices(result), full_start_index(i1) <= full_start_index(i2).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C8), (C11), (C17), (C19-C21), (C23) |
| (I2) | start_indices |
tensor thuộc loại số nguyên | (C2-C3), (C14), (C17), (C22) |
| (I3) | offset_dims |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C4-C5), (C22) |
| (I4) | collapsed_slice_dims |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C6-C9), (C22) |
| (I5) | operand_batching_dims |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C6), (C10-C12), (C16-C18), (C22) |
| (I6) | start_indices_batching_dims |
Hằng số tensor 1 chiều thuộc loại si64 |
(C13 – C17) |
| (I7) | start_index_map |
Hằng số tensor 1 chiều thuộc loại si64 |
(C3), (C18 – C19) |
| (I8) | index_vector_dim |
hằng số thuộc loại si64 |
(C2-C3), (C15), (C22) |
| (I9) | slice_sizes |
Hằng số tensor 1 chiều thuộc loại si64 |
(C9), (C12), (C20-C22) |
| (I10) | indices_are_sorted |
hằng số thuộc loại i1 |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C5), (C22-C23) |
Giới hạn
- (C1)
rank(operand) = size(offset_dims) + size(collapsed_slice_dims) + size(operand_batching_dims). - (C2)
0 <= index_vector_dim <= rank(start_indices). - (C3)
size(start_index_map) = index_vector_dim < rank(start_indices) ? dim(start_indices, index_vector_dim) : 1. - (C4)
is_unique(offset_dims) and is_sorted(offset_dims). - (C5)
0 <= offset_dims < rank(result). - (C6)
is_unique(concatenate(collapsed_slice_dims, operand_batching_dims)) - (C7)
is_sorted(collapsed_slice_dims). - (C8)
0 <= collapsed_slice_dims < rank(operand). - (C9)
slice_sizes[collapsed_slice_dims...] <= 1. - (C10)
is_sorted(operand_batching_dims). - (C11)
0 <= operand_batching_dims < rank(operand). - (C12)
slice_sizes[operand_batching_dims...] <= 1. - (C13)
is_unique(start_indices_batching_dims). - (C14)
0 <= start_indices_batching_dims < rank(start_indices). - (C15)
index_vector_dim not in start_indices_batching_dims. - (C16)
size(operand_batching_dims) == size(start_indices_batching_dims). - (C17)
dim(operand, operand_batching_dims...) = dim(start_indices, start_indices_batching_dims...). - (C18)
is_unique(concatenate(start_index_map, operand_batching_dims)). - (C19)
0 <= start_index_map < rank(operand). - (C20)
size(slice_sizes) = rank(operand). - (C21)
0 <= slice_sizes <= shape(operand). - (C22)
shape(result) = combine(batch_dim_sizes, offset_dim_sizes)trong đó:batch_dim_sizes = shape(start_indices), ngoại trừ kích thước phương diệnstart_indicestương ứng vớiindex_vector_dimkhông được đưa vào.offset_dim_sizes = slice_sizes, ngoại trừ kích thước phương diện trongslice_sizestương ứng vớicollapsed_slice_dimsvàoperand_batching_dimskhông được đưa vào.combineđặtbatch_dim_sizestại các trục tương ứng vớibatch_dimsvàoffset_dim_sizestại các trục tương ứng vớioffset_dims.
- (C23)
element_type(operand) = element_type(result).
Ví dụ
// %operand: [
// [
// [[1, 2], [3, 4], [5, 6], [7, 8]],
// [[9, 10],[11, 12], [13, 14], [15, 16]],
// [[17, 18], [19, 20], [21, 22], [23, 24]]
// ],
// [
// [[25, 26], [27, 28], [29, 30], [31, 32]],
// [[33, 34], [35, 36], [37, 38], [39, 40]],
// [[41, 42], [43, 44], [45, 46], [47, 48]]
// ]
// ]
// %start_indices: [
// [
// [[0, 0], [1, 0], [2, 1]],
// [[0, 1], [1, 1], [0, 9]]
// ],
// [
// [[0, 0], [2, 1], [2, 2]],
// [[1, 2], [0, 1], [1, 0]]
// ]
// ]
%result = "stablehlo.gather"(%operand, %start_indices) {
dimension_numbers = #stablehlo.gather<
offset_dims = [3, 4],
collapsed_slice_dims = [1],
operand_batching_dims = [0],
start_indices_batching_dims = [1],
start_index_map = [2, 1],
index_vector_dim = 3>,
slice_sizes = array<i64: 1, 1, 2, 2>,
indices_are_sorted = false
} : (tensor<2x3x4x2xi32>, tensor<2x2x3x2xi64>) -> tensor<2x2x3x2x2xi32>
// %result: [
// [
// [
// [[1, 2], [3, 4]],
// [[3, 4], [5, 6]],
// [[13, 14], [15, 16]]
// ],
// [
// [[33, 34], [35, 36]],
// [[35, 36], [37, 38]],
// [[41, 42], [43, 44]]
// ]
// ],
// [
// [
// [[1, 2], [3, 4]],
// [[13, 14], [15, 16]],
// [[21, 22], [23, 24]]
// ],
// [
// [[43, 44], [45, 46]],
// [[33, 34], [35, 36]],
// [[27, 28], [29, 30]]
// ]
// ]
// ]
get_dimension_size
Ngữ nghĩa
Tạo ra kích thước của dimension đã cho của operand. Nói một cách trang trọng hơn, result = dim(operand, dimension). Ngữ nghĩa chỉ liên quan đến thành phần hình dạng của loại. Loại phần tử có thể là bất kỳ loại nào.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor được lượng tử hoá | (C1) |
| (I2) | dimension |
hằng số thuộc loại si64 |
(C1) |
Kết quả đầu ra
| Tên | Loại |
|---|---|
result |
Tensor 0 chiều thuộc loại si32 |
Giới hạn
- (C1)
0 <= dimension < rank(operand).
Ví dụ
// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.get_dimension_size"(%operand) {
dimension = 1 : i64
} : (tensor<2x3xi64>) -> tensor<i32>
// %result: 3
get_tuple_element
Ngữ nghĩa
Trích xuất phần tử ở vị trí index của bộ operand và tạo ra một result. Nói một cách trang trọng hơn, result = operand[index].
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tuple | (C1), (C2) |
| (I2) | index |
hằng số thuộc loại si32 |
(C1), (C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
giá trị bất kỳ | (C2) |
Giới hạn
- (C1)
0 <= index < size(operand). - (C2)
type(result) = tuple_element_types(operand)[index].
Ví dụ
// %operand: ([1.0, 2.0], (3))
%result = "stablehlo.get_tuple_element"(%operand) <{index = 0 : i32}> : (tuple<tensor<2xf64>, tuple<tensor<i64>>>) -> tensor<2xf64>
// %result: [1.0, 2.0]
nếu
Ngữ nghĩa
Tạo ra kết quả đầu ra từ việc thực thi chính xác một hàm từ true_branch hoặc false_branch tuỳ thuộc vào giá trị của pred. Nói một cách trang trọng hơn, result =
pred ? true_branch() : false_branch().
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | pred |
Tensor 0 chiều thuộc loại i1 |
|
| (I2) | true_branch |
hàm | (C1-C3) |
| (I3) | false_branch |
hàm | (C1), (C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor, tensor lượng tử hoá hoặc mã thông báo có thể thay đổi | (C3) |
Giới hạn
- (C1)
input_types(true_branch) = input_types(false_branch) = []. - (C2)
output_types(true_branch) = output_types(false_branch). - (C3)
type(results...) = output_types(true_branch).
Ví dụ
// %result_true_branch: 10
// %result_false_branch: 11
// %pred: true
%result = "stablehlo.if"(%pred) ({
"stablehlo.return"(%result_true_branch) : (tensor<i32>) -> ()
}, {
"stablehlo.return"(%result_false_branch) : (tensor<i32>) -> ()
}) : (tensor<i1>) -> tensor<i32>
// %result: 10
imag
Ngữ nghĩa
Trích xuất phần ảo theo phần tử từ operand và tạo ra một tensor result. Cụ thể hơn, đối với mỗi phần tử x: imag(x) = is_complex(x) ? imaginary_part(x) :
constant(0, element_type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại số phức hoặc dấu phẩy động | (C1), (C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động | (C1), (C2) |
Giới hạn
- (C1)
shape(result) = shape(operand). - (C2)
element_type(result)được xác định là:complex_element_type(element_type(operand))nếuis_complex(operand).- Nếu không thì là
element_type(operand).
Ví dụ
// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.imag"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [2.0, 4.0]
trong nguồn cấp dữ liệu
Ngữ nghĩa
Đọc dữ liệu từ nguồn cấp dữ liệu trong luồng và tạo ra results.
Ngữ nghĩa của infeed_config được xác định theo cách triển khai.
results bao gồm các giá trị tải trọng xuất hiện trước và mã thông báo xuất hiện sau cùng. Trong tương lai, chúng tôi dự định chia tải trọng và mã thông báo thành hai đầu ra riêng biệt để cải thiện độ rõ ràng (#670).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại |
|---|---|---|
| (I1) | token |
token |
| (I2) | infeed_config |
hằng số thuộc loại string |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor, tensor lượng tử hoá hoặc mã thông báo có thể thay đổi | (C1-C3) |
Giới hạn
- (C1)
0 < size(results). - (C2)
is_empty(result[:-1])hoặ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 vào một tenxơ output các giá trị theo thứ tự tăng dần bắt đầu từ 0 dọc theo phương diện iota_dimension. Nói một cách trang trọng hơn,
output[output_index] = constant(is_quantized(output) ?
quantize(output_index[iota_dimension], element_type(output)) :
output_index[iota_dimension], element_type(output)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | iota_dimension |
si64 |
(C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
output |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
0 <= iota_dimension < rank(output).
Ví dụ
%output = "stablehlo.iota"() {
iota_dimension = 0 : i64
} : () -> tensor<4x5xi32>
// %output: [
// [0, 0, 0, 0, 0],
// [1, 1, 1, 1, 1],
// [2, 2, 2, 2, 2],
// [3, 3, 3, 3, 3]
// ]
%output = "stablehlo.iota"() {
iota_dimension = 1 : i64
} : () -> tensor<4x5xi32>
// %output: [
// [0, 1, 2, 3, 4],
// [0, 1, 2, 3, 4],
// [0, 1, 2, 3, 4],
// [0, 1, 2, 3, 4]
// ]
is_finite
Ngữ nghĩa
Thực hiện kiểm tra từng phần tử xem giá trị trong x có hữu hạn hay không (tức là không phải +Inf, -Inf hoặc NaN) và tạo ra một tensor y. Triển khai thao tác isFinite theo quy cách IEEE-754. Đối với các loại được phân lượng hoá, kết quả luôn là true.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | x |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
y |
tensor thuộc loại boolean | (C1) |
Giới hạn
- (C1)
shape(x) = shape(y).
Ví dụ
// Logical values: -Inf, +Inf, NaN, ...
// %x: [0xFFF0000000000000, 0x7FF0000000000000, 0x7FF8000000000000, -10.0, -0.0, 0.0, 10.0]
%y = "stablehlo.is_finite"(%x) : (tensor<7xf64) -> tensor<7xi1>
// %y: [false, false, false, true, true, true, true]
log
Ngữ nghĩa
Thực hiện phép toán lôgarit trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
logtừ IEEE-754. - Đối với số phức: lôgarit phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(log, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [[1.0, 2.0], [3.0, 4.0]]
%result = "stablehlo.log"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.0, 0.69314718055994529], [1.0986122886681098, 1.3862943611198906]]
log_plus_one
Ngữ nghĩa
Thực hiện phép toán lôgarit cộng một trên tensor operand và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
logp1từ IEEE-754. - Đối với số phức:
complex(log(hypot(real(x) + 1, imag(x))), atan2(imag(x), real(x) + 1)) - Đối với các loại được lượng tử hoá:
dequantize_op_quantize(log_plus_one, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [0.0, -0.999, 7.0, 6.38905621, 15.0]
%result = "stablehlo.log_plus_one"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [0.0, -6.90776825, 2.07944155, 2.0, 2.77258873]
logistic
Ngữ nghĩa
Thực hiện phép toán logistic theo phần tử trên tensor operand và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
division(1, addition(1, exp(-x)))từ IEEE-754. - Đối với số phức: hàm logistic phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(logistic, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.logistic"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.5, 0.73105858], [0.88079708, 0.95257413]]
map
Ngữ nghĩa
Áp dụng hàm ánh xạ computation cho inputs dọc theo dimensions và tạo ra một tensor result.
Nói một cách trang trọng hơn, result[result_index] = computation(inputs...[result_index]).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | inputs |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C4) |
| (I2) | dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C3) |
| (I3) | computation |
hàm | (C4) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1), (C4) |
Giới hạn
- (C1)
shape(inputs...) = shape(result). - (C2)
0 < size(inputs) = N. - (C3)
dimensions = range(rank(inputs[0])). - (C4)
computationcó loại(tensor<E0>, ..., tensor<EN-1>) -> tensor<E'>, trong đóEi = element_type(inputs[i])vàE' = element_type(result).
Ví dụ
// %input0: [[0, 1], [2, 3]]
// %input1: [[4, 5], [6, 7]]
%result = "stablehlo.map"(%input0, %input1) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = stablehlo.multiply %arg0, %arg1 : tensor<i64>
stablehlo.return %0 : tensor<i64>
}) {
dimensions = array<i64: 0, 1>
} : (tensor<2x2xi64>, tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[0, 5], [12, 21]]
tối đa
Ngữ nghĩa
Thực hiện phép toán max theo 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 giá trị boolean: OR logic.
- Đối với số nguyên: số nguyên tối đa.
- Đối với số thực:
maximumtừ IEEE-754. - Đối với số phức: giá trị tối đa theo thứ tự từ điển cho cặp
(real, imaginary). Việc áp đặt một thứ tự cho các số phức liên quan đến ngữ nghĩa đáng ngạc nhiên, vì vậy trong tương lai, chúng tôi dự định xoá hỗ trợ cho các số phức đối với thao tác này (#560). - Đối với các loại được lượng tử hoá:
dequantize_op_quantize(maximum, lhs, rhs, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1) |
| (I2) | rhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).
Ví dụ
// %lhs: [[1, 2], [7, 8]]
// %rhs: [[5, 6], [3, 4]]
%result = "stablehlo.maximum"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 8]]
tối thiểu
Ngữ nghĩa
Thực hiện phép toán min theo phần tử trên các tensor 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 giá trị boolean: phép toán AND logic.
- Đối với số nguyên: số nguyên tối thiểu.
- Đối với số thực:
minimumtừ IEEE-754. - Đối với số phức: giá trị tối thiểu theo thứ tự từ điển cho cặp
(real, imaginary). Việc áp đặt một thứ tự cho các số phức liên quan đến ngữ nghĩa đáng ngạc nhiên, vì vậy trong tương lai, chúng tôi dự định xoá hỗ trợ cho các số phức đối với thao tác này (#560). - Đối với các loại được lượng tử hoá:
dequantize_op_quantize(minimum, lhs, rhs, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1) |
| (I2) | rhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).
Ví dụ
// %lhs: [[1, 2], [7, 8]]
// %rhs: [[5, 6], [3, 4]]
%result = "stablehlo.minimum"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[1, 2], [3, 4]]
nhân
Ngữ nghĩa
Thực hiện phép nhân từng 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 giá trị boolean: phép toán AND logic.
- Đối với số nguyên: phép nhân số nguyên.
- Đối với số thực:
multiplicationtừ IEEE-754. - Đối với số phức: phép nhân số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(multiply, lhs, rhs, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1) |
| (I2) | rhs |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.multiply"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 12], [21, 32]]
phủ định
Ngữ nghĩa
Thực hiện phép phủ định theo phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số nguyên có dấu: phủ định số nguyên.
- Đối với số nguyên không dấu: bitcast thành số nguyên có dấu, phủ định số nguyên, bitcast trở lại số nguyên không dấu.
- Đối với số thực:
negatetừ IEEE-754. - Đối với số phức: phủ định số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(negate, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// Negation operation with integer Tensors
// %operand: [0, -2]
%result = "stablehlo.negate"(%operand) : (tensor<2xi32>) -> tensor<2xi32>
// %result: [0, 2]
// Negation operation with with complex tensors
// %operand: (2.5, 0.0)
%result = "stablehlo.negate"(%operand) : (tensor<1xcomplex<f32>>) -> tensor<1xcomplex<f32>>
// %result: [-2.5, -0.0]
không
Ngữ nghĩa
Thực hiện phép toán NOT theo phần tử của tensor operand và tạo ra một tensor result.
Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với giá trị boolean: phủ định trong logic.
- Đối với số nguyên: bitwise NOT.
Đối số
| Tên | Loại | Giới hạn |
|---|---|---|
operand |
tensor thuộc loại boolean hoặc số nguyên | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại boolean hoặc số nguyên | (C1) |
Giới hạn
- (C1)
type(operand) = type(result).
Ví dụ
// Bitwise operation with with integer tensors
// %operand: [[1, 2], [3, 4]]
%result = "stablehlo.not"(%operand) : (tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[-2, -3], [-4, -5]]
// Bitwise operation with with boolean tensors
// %operand: [true, false]
%result = "stablehlo.not"(%operand) : (tensor<2xi1>) -> tensor<2xi1>
// %result: [false, true]
optimization_barrier
Ngữ nghĩa
Đảm bảo rằng các thao tác tạo ra operand được thực thi trước mọi thao tác phụ thuộc vào result và ngăn các hoạt động chuyển đổi của trình biên dịch di chuyển các thao tác qua rào cản. Ngoài ra, thao tác này là một danh tính, tức là result = operand.
Đối số
| Tên | Loại | Giới hạn |
|---|---|---|
operand |
số lượng tensor thay đổi, tensor hoặc mã thông báo được lượng tử hoá theo từng tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
số lượng tensor thay đổi, tensor hoặc mã thông báo được lượng tử hoá theo từng tensor | (C1) |
Giới hạn
- (C1)
type(operand...) = type(result...).
Ví dụ
// %operand0: 0.0
// %operand1: 1.0
%result0, %result1 = "stablehlo.optimization_barrier"(%operand0, %operand1) : (tensor<f32>, tensor<f32>) -> (tensor<f32>, tensor<f32>)
// %result0: 0.0
// %result1: 1.0
hoặc
Ngữ nghĩa
Thực hiện phép 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 giá trị boolean: OR logic.
- Đối với số nguyên: đảo bit OR.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại số nguyên hoặc boolean | (C1) |
| (I2) | rhs |
tensor thuộc loại số nguyên hoặc boolean | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên hoặc boolean | (C1) |
Giới hạn
- (C1)
type(lhs) = type(rhs) = type(result).
Ví dụ
// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 12]]
// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, true]]
outfeed
Ngữ nghĩa
Ghi inputs vào nguồn cấp dữ liệu bên ngoài và tạo mã thông báo result.
Ngữ nghĩa của outfeed_config được xác định theo cách triển khai.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại |
|---|---|---|
| (I1) | inputs |
số lượng tensor hoặc tensor lượng tử hoá có thể thay đổi |
| (I2) | token |
token |
| (I3) | outfeed_config |
hằng số thuộc loại string |
Kết quả đầu ra
| Tên | Loại |
|---|---|
result |
token |
Ví dụ
%result = "stablehlo.outfeed"(%input0, %token) {
outfeed_config = ""
} : (tensor<2x2x2xi64>, !stablehlo.token) -> !stablehlo.token
miếng đệm
Ngữ nghĩa
Mở rộng operand bằng cách thêm khoảng đệm xung quanh tensor cũng như giữa các phần tử của tensor bằng padding_value đã cho.
edge_padding_low và edge_padding_high lần lượt chỉ định lượng khoảng đệm được thêm vào ở đầu dưới (bên cạnh chỉ mục 0) và đầu trên (bên cạnh chỉ mục cao nhất) của mỗi phương diện. Lượng khoảng đệm có thể là số âm, trong đó giá trị tuyệt đối của khoảng đệm âm cho biết số lượng phần tử cần xoá khỏi phương diện được chỉ định.
interior_padding chỉ định lượng khoảng đệm được thêm giữa hai phần tử bất kỳ trong mỗi phương diện (không được âm). Khoảng đệm bên trong xuất hiện trước khoảng đệm cạnh để khoảng đệm cạnh âm sẽ xoá các phần tử khỏi toán hạng có khoảng đệm bên trong.
Cụ thể hơn, result[result_index] được xác định là:
operand[operand_index]nếuresult_index = edge_padding_low + operand_index * (interior_padding + 1).- Nếu không thì là
padding_value.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C2), (C4) |
| (I2) | padding_value |
Tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
| (I3) | edge_padding_low |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C4) |
| (I4) | edge_padding_high |
Hằng số tensor 1 chiều thuộc loại si64 |
(C1), (C4) |
| (I5) | interior_padding |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2-C4) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C3 – C6) |
Giới hạn
- (C1)
element_type(operand) = element_type(padding_value) = element_type(result). - (C2)
size(edge_padding_low) = size(edge_padding_high) = size(interior_padding) = rank(operand). - (C3)
0 <= interior_padding. - (C4)
shape(result) = shape(operand) + edge_padding_low + max(shape(operand) - 1, 0) * interior_padding + edge_padding_high.
Ví dụ
// %operand: [
// [1, 2, 3],
// [4, 5, 6]
// ]
// %padding_value: 0
%result = "stablehlo.pad"(%operand, %padding_value) {
edge_padding_low = array<i64: 0, 1>,
edge_padding_high = array<i64: 2, 1>,
interior_padding = array<i64: 1, 2>
} : (tensor<2x3xi32>, tensor<i32>) -> tensor<5x9xi32>
// %result: [
// [0, 1, 0, 0, 2, 0, 0, 3, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0],
// [0, 4, 0, 0, 5, 0, 0, 6, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0]
// ]
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 việc đếm theo phần tử số lượng bit được đặt trong tenxơ operand và tạo ra một tenxơ result.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại số nguyên | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên | (C1) |
Giới hạn
- (C1)
type(operand) = type(result).
Ví dụ
// %operand: [0, 1, 2, 127]
%result = "stablehlo.popcnt"(%operand) : (tensor<4xi64>) -> tensor<4xi64>
// %result: [0, 1, 1, 7]
công suất
Ngữ nghĩa
Thực hiện phép luỹ thừa theo phần tử của tensor lhs bằng tensor rhs và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số nguyên: luỹ thừa số nguyên.
- Đối với số thực:
powtừ IEEE-754. - Đối với số phức: hàm phân phối mũ phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(power, lhs, rhs, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
| (I2) | rhs |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %lhs: [-2.0, -0.0, -36.0, 5.0, 3.0, 10000.0]
// %rhs: [2.0, 2.0, 1.1, 2.0, -1.0, 10.0]
%result = "stablehlo.power"(%lhs, %rhs) : (tensor<6xf64>, tensor<6xf64>) -> tensor<6xf64>
// %result: [4.0, 0.0, -nan, 25.0, 0.333333343, inf]
thực
Ngữ nghĩa
Trích xuất phần thực, theo từng phần tử, từ operand và tạo ra một tensor result. Cụ thể hơn, đối với mỗi phần tử x: real(x) = is_complex(x) ? real_part(x) : x.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại số phức hoặc dấu phẩy động | (C1), (C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động | (C1), (C2) |
Giới hạn
- (C1)
shape(result) = shape(operand). - (C2)
element_type(result)được xác định là:complex_element_type(element_type(operand))nếuis_complex(operand).- Nếu không thì là
element_type(operand).
Ví dụ
// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.real"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [1.0, 3.0]
recv
Ngữ nghĩa
Nhận dữ liệu từ một kênh có channel_id và tạo ra results.
Nếu is_host_transfer là true, thì thao tác này sẽ chuyển dữ liệu từ máy chủ. Nếu không, dữ liệu sẽ được chuyển từ một thiết bị khác dựa trên các giá trị của source_target_pairs. Cờ này trùng lặp thông tin được cung cấp trong channel_type, vì vậy, trong tương lai, chúng tôi dự định chỉ giữ lại một trong hai cờ này (#666). Nếu is_host_transfer = false và source_target_pairs là None hoặc trống, thì đó được coi là hành vi không xác định.
results bao gồm các giá trị tải trọng xuất hiện trước và mã thông báo xuất hiện sau cùng. Trong tương lai, chúng tôi dự định chia tải trọng và mã thông báo thành hai đầu ra riêng biệt để cải thiện độ rõ ràng (#670).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | token |
token |
|
| (I2) | channel_id |
hằng số thuộc loại si64 |
|
| (I3) | channel_type |
enum của DEVICE_TO_DEVICE và DEVICE_TO_HOST |
(C5) |
| (I4) | is_host_transfer |
hằng số thuộc loại i1 |
(C5-C6) |
| (I5) | source_target_pairs |
Hằng số tensor 2 chiều thuộc loại si64 |
(C1-C4), (C6) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor, tensor lượng tử hoá hoặc mã thông báo có thể thay đổi | (C2-C4) |
Giới hạn
- (C1)
dim(source_target_pairs, 1) = 2. - (C2)
is_unique(source_target_pairs[:, 0]). - (C3)
is_unique(source_target_pairs[:, 1]). - (C4)
0 <= source_target_pairs < N, trong đóNđược xác định là:num_replicasnếu bạn sử dụngcross_replica.num_partitionsnếu bạn sử dụngcross_partition.
- (C5)
channel_typeđược xác định là:DEVICE_TO_HOSTnếuis_host_transfer = true,- Nếu không thì là
DEVICE_TO_DEVICE.
Ví dụ
%results0, %results1 = "stablehlo.recv"(%token) {
channel_handle = #stablehlo.channel_handle<handle = 0, type = 1>,
is_host_transfer = false,
source_target_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
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 được xác định theo cách triển khai, tức là body và init_values phải tạo thành một đơn nguyên để đảm bảo rằng thao tác này tạo ra cùng một kết quả cho tất cả các đầu vào trên tất cả các cách triển khai. Tuy nhiên, điều kiện này không áp dụng cho nhiều phép quy giản phổ biến. Ví dụ: phép cộng dấu phẩy động cho body và 0 cho init_values thực sự không tạo thành một đơn thể vì phép cộng dấu phẩy động không có tính kết hợp.
Một cách chính thức hơn, results...[j0, ..., jR-1] = reduce(input_slices_converted) trong đó:
input_slices = inputs...[j0, ..., :, ..., jR-1], trong đó:được chèn vàodimensions.input_slices_converted = to_destination_type(input_slices..., type(func_inputs(body)[:len(func_inputs(body))//2])...).init_values_converted = to_destination_type(init_values..., type(func_inputs(body)[len(func_inputs(body))//2:])...).reduce(input_slices_converted) = exec(schedule)cho một số cây nhị phânscheduletrong đó:exec(node) = body(exec(node.left), exec(node.right)).exec(leaf) = leaf.value.
schedulelà một cây nhị phân đầy đủ do quá trình triển khai xác định, có thứ tự duyệt theo thứ tự bao gồm:- Giá trị
input_slices_converted...[index]cho tất cảindextrongindex_space(input_slices_converted)theo thứ tự từ điển tăng dần củaindex. - Được xen kẽ với một lượng
init_values_converteddo quá trình triển khai xác định ở các vị trí do quá trình triển khai xác định.
- Giá trị
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | inputs |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C4), (C6), (C7) |
| (I2) | init_values |
số lượng biến đổi của tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor | (C2), (C3) |
| (I3) | dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C4), (C5), (C7) |
| (I4) | body |
hàm | (C6) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C3), (C7), (C8) |
Giới hạn
- (C1)
same(shape(inputs...)). - (C2)
element_type(inputs...) = element_type(init_values...). - (C3)
0 < size(inputs) = size(init_values) = size(results) = N. - (C4)
0 <= dimensions < rank(inputs[0]). - (C5)
is_unique(dimensions). - (C6)
bodycó kiểu(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)trong đóis_promotable(element_type(inputs[i]), Ei). - (C7)
shape(results...) = shape(inputs...), ngoại trừ kích thước phương diện củainputs...tương ứng vớidimensionskhông được đưa vào. - (C8)
element_type(results[i]) = Eicho tất cảitrong[0,N).
Ví dụ
// %input = [[0, 1, 2, 3, 4, 5]]
// %init_value = 0
%result = "stablehlo.reduce"(%input, %init_value) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
dimensions = array<i64: 1>
} : (tensor<1x6xi64>, tensor<i64>) -> tensor<1xi64>
// %result = [15]
reduce_precision
Ngữ nghĩa
Thực hiện chuyển đổi từng phần tử của operand sang một loại dấu phẩy động khác sử dụng exponent_bits và mantissa_bits rồi quay lại loại dấu phẩy động ban đầu và tạo ra một tensor output.
Một cách trang trọng hơn:
- Các bit phần định trị của giá trị ban đầu được cập nhật để làm tròn giá trị ban đầu thành giá trị gần nhất có thể biểu thị bằng
mantissa_bitsbằng cách sử dụng ngữ nghĩaroundToIntegralTiesToEven. - Sau đó, nếu
mantissa_bitsnhỏ hơn số lượng bit phần định trị của giá trị ban đầu, thì các bit phần định trị sẽ bị cắt bớt thànhmantissa_bits. - Sau đó, nếu các bit số mũ của kết quả trung gian không nằm trong phạm vi do
exponent_bitscung cấp, thì kết quả trung gian sẽ tràn sang vô cùng bằng cách sử dụng dấu ban đầu hoặc tràn xuống 0 bằng cách sử dụng dấu ban đầu. - Đối với các loại được lượng tử hoá, hãy thực hiện
dequantize_op_quantize( lambda operand: reduce_precision(operand, exponent_bits, mantissa_bits), operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
| (I2) | exponent_bits |
hằng số thuộc loại si32 |
(C2) |
| (I3) | mantissa_bits |
hằng số thuộc loại si32 |
(C3) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
output |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(output). - (C2)
1 <= exponent_bits. - (C3)
0 <= mantissa_bits.
Ví dụ
// Logical values: +Inf, NaN, +Denormal, 0.0, 65519.0, 65520.0
// %operand: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0x0000000000000001, 0.0, 65519.0, 65520.0]
%output = "stablehlo.reduce_precision"(%operand) {
exponent_bits = 5 : i32,
mantissa_bits = 10 : i32
} : (tensor<6xf64>) -> tensor<6xf64>
// Logical values: +Inf, NaN, 0.0, 0.0, 65504.0, +Inf
// %output: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0.0, 0.0, 65504.0, 0x7FF0000000000000]
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 thao tác giảm bằng cách sử dụng computations trên các giá trị của tensor operand từ mỗi quy trình, chia kết quả giảm dọc theo scatter_dimension thành các phần và phân tán các phần đã chia giữa các quy trình để tạo ra result.
Thao tác này chia lưới quy trình StableHLO thành process_groups được xác định như sau:
cross_replica(replica_groups)nếuchannel_id <= 0 and use_global_device_ids = false.cross_replica_and_partition(replica_groups)nếuchannel_id > 0 and use_global_device_ids = false.flattened_ids(replica_groups)nếuchannel_id > 0 and use_global_device_ids = true.
Sau đó, trong mỗi process_group:
reduced_value = all_reduce(operand, replica_groups, channel_id, use_global_device_ids, computation).parts@sender = split(reduced_value@sender, dim(process_groups, 1), scatter_dimension).result@receiver = parts@sender[receiver_index]cho tất cảsendertrongprocess_group, trong đóreceiver_index = process_group.index(receiver).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C2), (C7), (C8) |
| (I2) | scatter_dimension |
hằng số thuộc loại si64 |
(C1), (C2), (C8) |
| (I3) | replica_groups |
Hằng số tensor 2 chiều thuộc loại si64 |
(C3 – C5) |
| (I4) | channel_id |
hằng số thuộc loại si64 |
(C6) |
| (I5) | use_global_device_ids |
hằng số thuộc loại i1 |
(C6) |
| (I6) | computation |
hàm | (C7) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C8-C9) |
Giới hạn
- (C1)
dim(operand, scatter_dimension) % dim(process_groups, 1) = 0. - (C2)
0 <= scatter_dimension < rank(operand). - (C3)
is_unique(replica_groups). - (C4)
size(replica_groups)được xác định là:num_replicasnếu bạn sử dụngcross_replica.num_replicasnếu bạn sử dụngcross_replica_and_partition.num_processesnếu bạn sử dụngflattened_ids.
- (C5)
0 <= replica_groups < size(replica_groups). - (C6) Nếu
use_global_device_ids = true, thìchannel_id > 0. - (C7)
computationcó loại(tensor<E>, tensor<E>) -> (tensor<E>)trong đóis_promotable(element_type(operand), E). - (C8)
shape(result) = shape(operand)ngoại trừ:dim(result, scatter_dimension) = dim(operand, scatter_dimension) / dim(process_groups, 1).
- (C9)
element_type(result) = E.
Ví dụ
// num_replicas: 2
// num_partitions: 1
// %operand@(0, 0): [[1, 2, 3, 4],
// [5, 6, 7, 8]]
// %operand@(1, 0): [[9, 10, 11, 12],
// [13, 14, 15, 16]]
%result = "stablehlo.reduce_scatter"(%operand) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
scatter_dimension = 1 : i64,
replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor<2x4xi64>) -> tensor<2x2xi64>
//
// %result@(0, 0): [[10, 12],
// [18, 20]]
// %result@(1, 0): [[14, 16],
// [22, 24]]
reduce_window
Ngữ nghĩa
Áp dụng hàm giảm body cho các cửa sổ inputs và init_values, đồng thời tạo ra results.
Sơ đồ sau đây cho thấy cách các phần tử trong results... được tính toán từ inputs... bằng một ví dụ cụ thể.
Cụ thể hơn, results...[result_index] = reduce(windows, init_values, axes(inputs...), body)
(xem reduce) trong đó:
padded_inputs = pad(inputs..., init_values..., padding[:, 0], padding[:, 1], base_dilations - 1).window_start = result_index * window_strides.window_end = window_start + (window_dimensions - 1) * window_dilations + 1.windows = slice(padded_inputs..., window_start, window_end, window_dilations).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | inputs |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C4), (C6), (C8), (C10), (C12), (C13), (C15) |
| (I2) | init_values |
số lượng biến đổi của tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C13) |
| (I3) | window_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C4), (C5), (C15) |
| (I4) | window_strides |
Hằng số tensor 1 chiều thuộc loại si64 |
(C6), (C7), (C15) |
| (I5) | base_dilations |
Hằng số tensor 1 chiều thuộc loại si64 |
(C8), (C9), (C15) |
| (I6) | window_dilations |
Hằng số tensor 1 chiều thuộc loại si64 |
(C10), (C11), (C15) |
| (I7) | padding |
Hằng số tensor 2 chiều thuộc loại si64 |
(C12), (C15) |
| (I8) | body |
hàm | (C13) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C14-C16) |
Giới hạn
- (C1)
0 < size(inputs) = size(init_values) = size(results) = N. - (C2)
same(shape(inputs...)). - (C3)
element_type(inputs...) = element_type(init_values...). - (C4)
size(window_dimensions) = rank(inputs[0]). - (C5)
0 < window_dimensions. - (C6)
size(window_strides) = rank(inputs[0]). - (C7)
0 < window_strides. - (C8)
size(base_dilations) = rank(inputs[0]). - (C9)
0 < base_dilations. - (C10)
size(window_dilations) = rank(inputs[0]). - (C11)
0 < window_dilations. - (C12)
shape(padding) = [rank(inputs[0]), 2]. - (C13)
bodycó loại(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)trong đóis_promotable(element_type(inputs[i]), Ei). - (C14)
same(shape(results...)). - (C15)
shape(results[0]) = num_windowstrong đó:dilated_input_shape = shape(inputs[0]) = 0 ? 0 : (shape(inputs[0]) - 1) * base_dilations + 1.padded_input_shape = padding[:, 0] + dilated_input_shape + padding[:, 1].dilated_window_shape = (window_dimensions - 1) * window_dilations + 1.is_empty_window = padded_input_shape = 0 || dilated_window_shape > padded_input_shape.num_windows = is_empty_window ? 0 : floor((padded_input_shape - dilated_window_shape) / window_strides) + 1.
- (C16)
element_type(results[i]) = Eicho tất cảiở[0,N).
Ví dụ
// %input = [[1, 2], [3, 4], [5, 6]]
// %init_value = 0
%result = "stablehlo.reduce_window"(%input, %init_value) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
window_dimensions = array<i64: 2, 1>,
window_strides = array<i64: 4, 1>,
base_dilations = array<i64: 2, 1>,
window_dilations = array<i64: 3, 1>,
padding = dense<[[2, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<3x2xi64>, tensor<i64>) -> tensor<2x2xi64>
// %result = [[0, 0], [3, 4]]
phần dư
Ngữ nghĩa
Thực hiện phần dư theo phần tử của các tensor số bị chia lhs và số chia rhs, đồng thời tạo ra một tensor result.
Cụ thể hơn, dấu của kết quả được lấy từ số bị chia và giá trị tuyệt đối của kết quả luôn nhỏ hơn giá trị tuyệt đối của số chia.
Số dư được tính là lhs - d * rhs, trong đó d được tính theo công thức:
- Đối với số nguyên:
stablehlo.divide(lhs, rhs). - Đối với số thực:
division(lhs, rhs)từ IEEE-754 có thuộc tính làm trònroundTowardZero. - Đối với số phức: Sẽ xác định sau (#997).
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(remainder, lhs, rhs, type(result)).
Đối với các kiểu phần tử dấu phẩy động, thao tác này trái ngược với thao tác remainder trong quy cách IEEE-754, trong đó d là một giá trị nguyên gần với giá trị chính xác của lhs/rhs với các mối quan hệ ràng buộc với số chẵn.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
| (I2) | rhs |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %lhs: [17, -17, 17, -17]
// %rhs: [3, 3, -3, -3]
%result = "stablehlo.remainder"(%lhs, %rhs) : (tensor<4xi64>, tensor<4xi64>) -> tensor<4xi64>
// %result: [2, -2, 2, -2]
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>
reshape
Ngữ nghĩa
Thực hiện việc định hình lại tensor operand thành tensor result. Về mặt khái niệm, việc này tương đương với việc giữ nguyên cùng một biểu diễn chuẩn tắc nhưng có thể thay đổi hình dạng, ví dụ: từ tensor<2x3xf32> thành tensor<3x2xf32> hoặc tensor<6xf32>.
Nói một cách chính thức hơn, result[result_index] = operand[operand_index] trong đó result_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 được lượng tử hoá | (C1-C3) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá | (C1-C3) |
Giới hạn
- (C1)
element_type(result)được cho bởi:element_type(operand), nếu!is_per_axis_quantized(operand).element_type(operand), ngoại trừquantization_dimension(operand)vàquantization_dimension(result)có thể khác nhau.
- (C2)
size(operand) = size(result). - (C3) Nếu
is_per_axis_quantized(operand):reduce(dims(operand, [0, 1, ..., quantization_dimension(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [0, 1, ..., quantization_dimension(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).dim(operand, quantization_dimension(operand)) = dim(result, quantization_dimension(result)).reduce(dims(operand, [quantization_dimension(operand) + 1, ..., rank(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [quantization_dimension(result) + 1, ..., rank(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).
Ví dụ
// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.reshape"(%operand) : (tensor<2x3xi32>) -> tensor<3x2xi32>
// %result: [[1, 2], [3, 4], [5, 6]]
đảo ngược
Ngữ nghĩa
Đảo ngược thứ tự của các phần tử trong operand dọc theo dimensions đã chỉ định và tạo ra một tenxơ result. Một cách chính thức hơn, result[result_index] = operand[operand_index] trong đó:
operand_index[d] = dim(result, d) - result_index[d] - 1nếudtrongdimensions.- Nếu không thì là
operand_index[d] = result_index[d].
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C3) |
| (I2) | dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C3) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C3) |
Giới hạn
- (C1)
type(operand) = type(result). - (C2)
is_unique(dimensions). - (C3)
0 <= dimensions < rank(result).
Ví dụ
// %operand = [[1, 2], [3, 4], [5, 6]]
%result = "stablehlo.reverse"(%operand) {
dimensions = array<i64: 1>
} : (tensor<3x2xi32>) -> tensor<3x2xi32>
// %result: [[2, 1], [4, 3], [6, 5]]
rng
Ngữ nghĩa
Tạo số ngẫu nhiên bằng thuật toán rng_distribution và tạo ra một tenxơ result có hình dạng nhất định shape.
Nếu rng_distribution = UNIFORM, thì các số ngẫu nhiên được tạo theo phân phối đồng đều trong khoảng [a, b). Nếu a >= b, hành vi sẽ không xác định.
Nếu rng_distribution = NORMAL, thì các số ngẫu nhiên được tạo theo phân phối chuẩn với giá trị trung bình = a và độ lệch chuẩn = b.
Nếu b < 0, hành vi sẽ không xác định.
Cách chính xác để tạo số ngẫu nhiên được xác định theo quá trình triển khai. Ví dụ: chúng có thể là hàm xác định hoặc không, và có thể sử dụng hoặc không sử dụng trạng thái ẩn.
Trong các cuộc trò chuyện với nhiều bên liên quan, thao tác này đã được coi là không còn hiệu quả, vì vậy, trong tương lai, chúng tôi dự định sẽ loại bỏ thao tác này (#597).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | a |
Tensor 0 chiều thuộc loại số nguyên, boolean hoặc dấu phẩy động | (C1), (C2) |
| (I2) | b |
Tensor 0 chiều thuộc loại số nguyên, boolean hoặc dấu phẩy động | (C1), (C2) |
| (I3) | shape |
Hằng số tensor 1 chiều thuộc loại si64 |
(C3) |
| (I4) | rng_distribution |
enum của UNIFORM và NORMAL |
(C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên, boolean hoặc dấu phẩy động | (C1-C3) |
Giới hạn
- (C1)
element_type(a) = element_type(b) = element_type(result). - (C2) Nếu
rng_distribution = NORMAL, thìis_float(a). - (C3)
shape(result) = shape.
Ví dụ
// %a = 0
// %b = 2
// %shape = [3, 3]
%result = "stablehlo.rng"(%a, %b, %shape) {
rng_distribution = #stablehlo<rng_distribution UNIFORM>
} : (tensor<i32>, tensor<i32>, tensor<2xi64>) -> tensor<3x3xi32>
// %result: [
// [1, 0, 1],
// [1, 1, 1],
// [0, 0, 0]
// ]
rng_bit_generator
Ngữ nghĩa
Trả về một output chứa đầy các bit ngẫu nhiên đồng nhất và trạng thái đầu ra được cập nhật output_state bằng cách sử dụng thuật toán trình tạo số ngẫu nhiên giả rng_algorithm cho một trạng thái ban đầu initial_state. Đầu ra được đảm bảo là hàm xác định của initial_state, nhưng không được đảm bảo là xác định giữa các phương thức triển khai.
rng_algorithm là một trong những giá trị sau:
DEFAULT: Thuật toán do quá trình triển khai xác định.THREE_FRY: Biến thể do quá trình triển khai xác định của thuật toán Threefry.*PHILOX: Biến thể do quá trình triển khai xác định của thuật toán Philox.*
* Xem: Salmon và cộng sự, SC 2011. Số ngẫu nhiên song song: dễ dàng như đếm 1, 2, 3.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | rng_algorithm |
enum của DEFAULT, THREE_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 thuộc loại số nguyên hoặc dấu phẩy động |
Giới hạn
- (C1)
type(initial_state) = type(output_state). - (C2)
size(initial_state)được xác định là:- do quá trình triển khai xác định nếu
rng_algorithm = DEFAULT. 2nếurng_algorithm = THREE_FRY.2hoặc3nếurng_algorithm = PHILOX.
- do quá trình 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 thao tác làm tròn từng phần tử về số nguyên gần nhất, phá vỡ các mối liên kết từ 0, trên tenxơ operand và tạo ra tenxơ result. Triển khai thao tác roundToIntegralTiesToAway theo quy cách IEEE-754. Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(round_nearest_afz, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand = [-2.5, 0.4, 0.5, 0.6, 2.5]
%result = "stablehlo.round_nearest_afz"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [-3.0, 0.0, 1.0, 1.0, 3.0]
round_nearest_even
Ngữ nghĩa
Thực hiện việc làm tròn từng phần tử về số nguyên gần nhất, phá vỡ các mối liên kết về số nguyên chẵn, trên tenxơ operand và tạo ra một tenxơ result. Triển khai thao tác roundToIntegralTiesToEven theo quy cách IEEE-754. Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(round_nearest_even, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc tensor được lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand = [-2.5, 0.4, 0.5, 0.6, 2.5]
%result = "stablehlo.round_nearest_even"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [-2.0, 0.0, 0.0, 1.0, 2.0]
rsqrt
Ngữ nghĩa
Thực hiện phép toán căn bậc hai nghịch đảo theo phần tử trên tenxơ operand và tạo ra tenxơ result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
rSqrttừ IEEE-754. - Đối với số phức: căn bậc hai nghịch đảo phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(rsqrt, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [[1.0, 4.0], [9.0, 25.0]]
%result = "stablehlo.rsqrt"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[1.0, 0.5], [0.33333343, 0.2]]
phân tán
Ngữ nghĩa
Tạo ra các tensor results bằng với các tensor inputs, ngoại trừ một số lát cắt do scatter_indices chỉ định được cập nhật bằng các giá trị updates bằng cách sử dụng update_computation.
Sơ đồ sau đây cho thấy cách các phần tử trong updates... liên kết với các phần tử trong results... bằng một ví dụ cụ thể. Biểu đồ này chọn một vài chỉ mục updates... làm ví dụ và giải thích chi tiết chỉ mục results... mà chúng tương ứng.
Cụ thể hơn, đối với mọi update_index trong index_space(updates[0]):
update_scatter_dims = [d for d in axes(updates[0]) and d not in update_window_dims].update_scatter_index = update_index[update_scatter_dims...].start_indexđược xác định là:scatter_indices[si0, ..., :, ..., siN]trong đósilà các phần tử riêng lẻ trongupdate_scatter_indexvà:được chèn vào chỉ mụcindex_vector_dim, nếuindex_vector_dim<rank(scatter_indices).- Nếu không thì là
[scatter_indices[update_scatter_index]].
- Đối với
d_inputtrongaxes(inputs[0]),full_start_index[d_input] = start_index[d_start]nếud_input = scatter_dims_to_operand_dims[d_start].- Nếu không thì là
full_start_index[d_input] = 0.
- Đối với
d_inputtrongaxes(inputs[0]),full_batching_index[d_input] = update_scatter_index[d_start - (d_start < index_vector_dim ? 0 : 1)]nếud_input = input_batching_dims[i_batching]vàd_start = scatter_indices_batching_dims[i_batching].- Nếu không thì là
full_batching_index[d_input] = 0.
update_window_index = update_index[update_window_dims...].full_window_index = [wi0, ..., 0, ..., wiN]trong đówilà các phần tử riêng lẻ trongupdate_window_indexvà0được chèn ở các chỉ mục từinserted_window_dimsvàinput_batching_dims.result_index = full_start_index + full_batching_index + full_window_index.
Cho rằng results = exec(schedule, inputs), trong đó:
schedulelà một hoán vị do quá trình triển khai xác định củaindex_space(updates[0]).exec([update_index, ...], results) = exec([...], updated_results)nơi:- Nếu
result_indexnằm trong phạm vi củashape(results...) updates_converted = to_destination_type( updates...[update_index], type(func_inputs(update_computation) [len(func_inputs(update_computation))//2:])... )updated_values = update_computation(results...[result_index], updates_converted)updated_resultslà bản sao củaresultsvớiresults...[result_index]được đặt thànhupdated_values....- Hoặc
updated_results = results.
- Nếu
exec([], results) = results.
Nếu indices_are_sorted là true thì quá trình triển khai có thể giả định rằng scatter_indices được sắp xếp theo scatter_dims_to_operand_dims, nếu không thì hành vi sẽ không xác định. Nói một cách chính thức hơn, đối với tất cả i1 < i2 từ indices(result), full_start_index(i1) <= full_start_index(i2).
Nếu unique_indices là true thì quá trình triển khai có thể giả định rằng tất cả các chỉ mục result_index đang đượ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ố thuộc loại si64 |
(C4), (C16), (C19), (C22) |
| (I10) | indices_are_sorted |
hằng số thuộc loại i1 |
|
| (I11) | unique_indices |
hằng số thuộc loại i1 |
|
| (I12) | update_computation |
hàm | (C23) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C24-C25) |
Giới hạn
- (C1)
same(shape(inputs...)). - (C2)
rank(inputs[0]) = size(update_window_dims) + size(inserted_window_dims) + size(input_batching_dims). - (C3)
same(shape(updates...)). - (C4)
shape(updates[0]) = combine(update_scatter_dim_sizes, update_window_dim_sizes)trong đó:update_scatter_dim_sizes = shape(scatter_indices), ngoại trừ kích thước phương diện củascatter_indicestương ứng vớiindex_vector_dimkhông được đưa vào.update_window_dim_sizes <= shape(inputs[0]), ngoại trừ kích thước phương diện tronginputs[0]tương ứng vớiinserted_window_dimsvàinput_batching_dimskhông được đưa vào.combineđặtupdate_scatter_dim_sizestại các trục tương ứng vớiupdate_scatter_dimsvàupdate_window_dim_sizestại các trục tương ứng vớiupdate_window_dims.
- (C5)
0 < size(inputs) = size(updates) = N. - (C6)
element_type(updates...) = element_type(inputs...). - (C7)
is_unique(update_window_dims) and is_sorted(update_window_dims). - (C8)
0 <= update_window_dims < rank(updates[0]). - (C9)
is_unique(concatenate(inserted_window_dims, input_batching_dims)) - (C10)
is_sorted(inserted_window_dims). - (C11)
0 <= inserted_window_dims < rank(inputs[0]). - (C12)
is_sorted(input_batching_dims). - (C13)
0 <= input_batching_dims < rank(inputs[0])). - (C14)
is_unique(scatter_indices_batching_dims). - (C15)
0 <= scatter_indices_batching_dims < rank(scatter_indices). - (C16)
index_vector_dim not in scatter_indices_batching_dims. - (C17)
size(input_batching_dims) == size(scatter_indices_batching_dims). - (C18)
dim(inputs[0], input_batching_dims...) = dim(scatter_indices, scatter_indices_batching_dims...). - (C19)
size(scatter_dims_to_operand_dims) = index_vector_dim < rank(scatter_indices) ? dim(scatter_indices, index_vector_dim) : 1. - (C20)
is_unique(concatenate(scatter_dims_to_operand_dims, input_batching_dims)). - (C21)
0 <= scatter_dims_to_operand_dims < rank(inputs[0]). - (C22)
0 <= index_vector_dim <= rank(scatter_indices). - (C23)
update_computationcó kiểu(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>), trong đóis_promotable(element_type(inputs[i]), Ei). - (C24)
shape(inputs...) = shape(results...). - (C25)
element_type(results[i]) = Eicho tất cảitrong[0,N).
Ví dụ
// %input: [
// [
// [[1, 2], [3, 4], [5, 6], [7, 8]],
// [[9, 10],[11, 12], [13, 14], [15, 16]],
// [[17, 18], [19, 20], [21, 22], [23, 24]]
// ],
// [
// [[25, 26], [27, 28], [29, 30], [31, 32]],
// [[33, 34], [35, 36], [37, 38], [39, 40]],
// [[41, 42], [43, 44], [45, 46], [47, 48]]
// ]
// ]
// %scatter_indices: [
// [
// [[0, 0], [1, 0], [2, 1]],
// [[0, 1], [1, 1], [0, 9]]
// ],
// [
// [[0, 0], [2, 1], [2, 2]],
// [[1, 2], [0, 1], [1, 0]]
// ]
// ]
// %update: [
// [
// [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]],
// [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]]
// ],
// [
// [[[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 ra một tensor result trong đó mỗi phần tử được chọn từ tensor on_true hoặc on_false dựa trên giá trị của phần tử tương ứng của pred.
Cụ thể hơn, result[result_index] = pred_element ? on_true[result_index] :
on_false[result_index], trong đó pred_element = rank(pred) = 0 ? pred[] :
pred[result_index]. Đối với các loại được định lượng, hãy thực hiện dequantize_select_quantize(pred, on_true, on_false, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | pred |
tensor thuộc loại i1 |
(C1) |
| (I2) | on_true |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C1-C2) |
| (I3) | on_false |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C2) |
Giới hạn
- (C1)
rank(pred) = 0 or shape(pred) = shape(on_true). - (C2)
baseline_type(on_true) = baseline_type(on_false) = baseline_type(result).
Ví dụ
// %pred: [[false, true], [true, false]]
// %on_true: [[1, 2], [3, 4]]
// %on_false: [[5, 6], [7, 8]]
%result = "stablehlo.select"(%pred, %on_true, %on_false) : (tensor<2x2xi1>, tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 2], [3, 8]]
select_and_scatter
Ngữ nghĩa
Phân tán các giá trị từ tenxơ source bằng cách sử dụng scatter dựa trên kết quả của reduce_window của tenxơ input bằng cách sử dụng select và tạo ra một tenxơ result.
Sơ đồ sau đây cho thấy cách tính toán các phần tử trong result từ operand và source bằng một ví dụ cụ thể.
Một cách trang trọng hơn:
selected_values = reduce_window_without_init(...)với các đầu vào sau:inputs = [operand].window_dimensions,window_stridesvàpaddingđược sử dụng nguyên trạng.base_dilations = windows_dilations = 1.bodyđược xác định là:
def body(arg0: tensor<E>, arg1: tensor<E>) -> tensor<E>: return select(arg0, arg1) ? arg0 : arg1;trong đó
E = element_type(operand)vàreduce_window_without_inithoạt động giống hệt nhưreduce_window, ngoại trừschedulecủareducecơ bản (xem reduce) không bao gồm các giá trị init. Hiện tại, chưa xác định được điều gì sẽ xảy ra nếu cửa sổ tương ứng không có giá trị (#731).result[result_index] = reduce([source_values], [init_value], [0], scatter)trong đó:source_values = [source[source_index] for source_index in source_indices].selected_index(source_index) = operand_indexnếuselected_values[source_index]có phần tửoperandtừoperand_index.source_indices = [source_index for source_index in indices(source) if selected_index(source_index) = result_index].
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C4), (C6), (C8-C11) |
| (I2) | source |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C2) |
| (I3) | init_value |
Tensor 0 chiều hoặc tensor lượng tử hoá trên mỗi tensor | (C3) |
| (I4) | window_dimensions |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C4), (C5) |
| (I5) | window_strides |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C6), (C7) |
| (I6) | padding |
Hằng số tensor 2 chiều thuộc loại si64 |
(C2), (C8) |
| (I7) | select |
hàm | (C9) |
| (I8) | scatter |
hàm | (C10) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá theo từng tensor | (C11 – C12) |
Giới hạn
- (C1)
element_type(operand) = element_type(source). - (C2)
shape(source) = num_windowstrong đó:padded_operand_shape = padding[:, 0] + shape(operand) + padding[:, 1].is_empty_window = padded_operand_shape = 0 || window_dimensions > padded_operand_shape.num_windows = is_empty_window ? 0 : floor((padded_operand_shape - window_dimensions) / window_strides) + 1.
- (C3)
element_type(init_value) = element_type(operand). - (C4)
size(window_dimensions) = rank(operand). - (C5)
0 < window_dimensions. - (C6)
size(window_strides) = rank(operand). - (C7)
0 < window_strides. - (C8)
shape(padding) = [rank(operand), 2]. - (C9)
selectcó loại(tensor<E>, tensor<E>) -> tensor<i1>trong đóE = element_type(operand). - (C10)
scattercó loại(tensor<E>, tensor<E>) -> tensor<E>trong đóis_promotable(element_type(operand), E). - (C11)
shape(operand) = shape(result). - (C12)
element_type(result) = E.
Ví dụ
// %operand: [[1, 5], [2, 5], [3, 6], [4, 4]]
// %source: [[5, 6], [7, 8]]
// %init_value: 0
%result = "stablehlo.select_and_scatter"(%operand, %source, %init_value) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.compare"(%arg0, %arg1) {
comparison_direction = #stablehlo<comparison_direction GE>
} : (tensor<i64>, tensor<i64>) -> tensor<i1>
"stablehlo.return"(%0) : (tensor<i1>) -> ()
}, {
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
window_dimensions = array<i64: 3, 1>,
window_strides = array<i64: 2, 1>,
padding = dense<[[0, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<4x2xi64>, tensor<2x2xi64>, tensor<i64>) -> tensor<4x2xi64>
// %result: [[0, 0], [0, 0], [5, 14], [7, 0]]
gửi
Ngữ nghĩa
Gửi inputs đến một kênh channel_id. Sau đó, các đầu vào sẽ được gửi đến các thiết bị khác theo thứ tự được chỉ định bởi source_target_pairs. Thao tác này tạo ra một mã thông báo result.
Nếu is_host_transfer là true, thì thao tác sẽ chuyển dữ liệu đến máy chủ lưu trữ. Nếu không, dữ liệu sẽ được chuyển sang một thiết bị khác dựa trên các giá trị của source_target_pairs. Cờ này trùng lặp thông tin được cung cấp trong channel_type, vì vậy, trong tương lai, chúng tôi dự định chỉ giữ lại một trong hai cờ này (#666). Nếu is_host_transfer = false và source_target_pairs là None hoặc trống, thì đó được coi là hành vi không xác định.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | inputs |
số lượng tensor hoặc tensor lượng tử hoá có thể thay đổi | |
| (I2) | token |
token |
|
| (I3) | channel_id |
hằng số thuộc loại si64 |
|
| (I4) | channel_type |
enum của DEVICE_TO_DEVICE và DEVICE_TO_HOST |
(C5) |
| (I5) | is_host_transfer |
hằng số thuộc loại i1 |
(C5-C6) |
| (I6) | source_target_pairs |
Hằng số tensor 2 chiều thuộc loại si64 |
(C1-C4), (C6) |
Kết quả đầu ra
| Tên | Loại |
|---|---|
result |
token |
Giới hạn
- (C1)
dim(source_target_pairs, 1) = 2. - (C2)
is_unique(source_target_pairs[:, 0]). - (C3)
is_unique(source_target_pairs[:, 1]). - (C4)
0 <= source_target_pairs < N, trong đóNđược xác định là:num_replicasnếu bạn sử dụngcross_replica.num_partitionsnếu bạn sử dụngcross_partition.
- (C5)
channel_typeđược xác định là:DEVICE_TO_HOSTnếuis_host_transfer = true,- Nếu không thì là
DEVICE_TO_DEVICE.
Ví dụ
%result = "stablehlo.send"(%operand, %token) {
channel_handle = #stablehlo.channel_handle<handle = 0, type = 1>,
is_host_transfer = false,
source_target_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>
} : (tensor<2x2xi64>, !stablehlo.token) -> !stablehlo.token
shift_left
Ngữ nghĩa
Thực hiện thao tác dịch trái theo phần tử trên tensor lhs theo số lượng bit rhs và tạo ra tensor result.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại số nguyên | (C1) |
| (I2) | rhs |
tensor thuộc loại số nguyên | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên | (C1) |
Giới hạn
- (C1)
type(lhs) = type(rhs) = type(result).
Ví dụ
// %lhs: [-1, 0, 1]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_left"(%lhs, %rhs): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %result: [-2, 0, 8]
shift_right_arithmetic
Ngữ nghĩa
Thực hiện phép toán dịch chuyển số học sang phải theo từng phần tử trên tenxơ lhs theo số lượng bit rhs và tạo ra một tenxơ result.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại số nguyên | (C1) |
| (I2) | rhs |
tensor thuộc loại số nguyên | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên | (C1) |
Giới hạn
- (C1)
type(lhs) = type(rhs) = type(result).
Ví dụ
// %lhs: [-1, 0, 8]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_right_arithmetic"(%lhs, %rhs): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %result: [-1, 0, 1]
shift_right_logical
Ngữ nghĩa
Thực hiện thao tác dịch chuyển logic sang phải theo từng phần tử trên tenxơ lhs theo rhs số lượng bit và tạo ra một tenxơ result.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại số nguyên | (C1) |
| (I2) | rhs |
tensor thuộc loại số nguyên | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên | (C1) |
Giới hạn
- (C1)
type(lhs) = type(rhs) = type(result).
Ví dụ
// %lhs: [-1, 0, 8]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_right_logical"(%lhs, %rhs): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %result: [9223372036854775807, 0, 1]
biển báo
Ngữ nghĩa
Trả về dấu của phần tử operand theo từng phần tử và tạo ra một tensor result.
Cụ thể hơn, đối với mỗi phần tử x, ngữ nghĩa có thể được biểu thị bằng cú pháp Python như sau:
def sign(x):
if is_integer(x):
if compare(x, 0, LT, SIGNED): return -1
if compare(x, 0, EQ, SIGNED): return 0
return 1
elif is_float(x):
if is_nan(x): return NaN
if compare(x, -0.0, EQ, FLOAT): return -0.0
if compare(x, +0.0, EQ, FLOAT): return +0.0
if compare(x, 0.0, LT, FLOAT): return -1.0
return 1.0
elif is_complex(x):
if is_nan(real(x)) or is_nan(imag(x)): return (NaN, NaN)
if compare(x, (0.0, 0.0), EQ, FLOAT): return (0.0, 0.0)
return divide(x, convert(abs(x), type(x)))
Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(sign, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor của số nguyên có dấu, dấu phẩy động hoặc loại phức hợp hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor của số nguyên có dấu, dấu phẩy động hoặc loại phức hợp hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// Logical values: +NaN, -1.0, -0.0, +0.0, 1.0
// operand: [0x7FFFFFFFFFFFFFFF, -1.0, -0.0, 0.0, 1.0]
%result = "stablehlo.sign"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// Logical values: +NaN, -1.0, -0.0, +0.0, 1.0
// %result: [0x7FFFFFFFFFFFFFFF, -1.0, -0.0, 0.0, 1.0]
sin
Ngữ nghĩa
Thực hiện phép toán sin trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
sintừ IEEE-754. - Đối với số phức: sin phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(sine, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.sine"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [0.0, -1.0]]
lát cắt
Ngữ nghĩa
Trích xuất một lát cắt từ operand bằng cách sử dụng các chỉ mục bắt đầu được tính toán tĩnh và tạo ra một tensor result. start_indices chứa chỉ mục bắt đầu của lát cắt cho từng phương diện, limit_indices chứa chỉ mục kết thúc (loại trừ) cho lát cắt cho từng phương diện và strides chứa bước nhảy cho từng phương diện.
Cụ thể hơn, result[result_index] = operand[operand_index] trong đó operand_index = start_indices + result_index * strides.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C3), (C5) |
| (I2) | start_indices |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C3), (C5) |
| (I3) | limit_indices |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C3), (C5) |
| (I4) | strides |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2), (C4) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor lượng tử hoá trên mỗi tensor | (C1), (C5) |
Giới hạn
- (C1)
element_type(operand) = element_type(result). - (C2)
size(start_indices) = size(limit_indices) = size(strides) = rank(operand). - (C3)
0 <= start_indices <= limit_indices <= shape(operand). - (C4)
0 < strides. - (C5)
shape(result) = ceil((limit_indices - start_indices) / strides).
Ví dụ
// %operand: [
// [0, 0, 0, 0],
// [0, 0, 1, 1],
// [0, 0, 1, 1]
// ]
%result = "stablehlo.slice"(%operand) {
start_indices = array<i64: 1, 2>,
limit_indices = array<i64: 3, 4>,
strides = array<i64: 1, 1>
} : (tensor<3x4xi64>) -> tensor<2x2xi64>
// % result: [
// [1, 1],
// [1, 1]
// ]
sắp xếp
Ngữ nghĩa
Sắp xếp các phần tử 1 chiều của inputs dọc theo phương diện dimension với nhau, theo comparator và tạo ra results.
Không giống như các đầu vào tương tự trong các thao tác khác, dimension cho phép các giá trị âm, với ngữ nghĩa được mô tả bên dưới. Trong tương lai, điều này có thể không được phép vì lý do nhất quán (#1377).
Nếu is_stable là true, thì việc sắp xếp sẽ ổn định, tức là thứ tự tương đối của các phần tử được bộ so sánh coi là bằng nhau sẽ được giữ nguyên. Trong trường hợp chỉ có một đầu vào, hai phần tử e1 và e2 được bộ so sánh coi là bằng nhau khi và chỉ khi comparator(e1, e2) = comparator(e2, e1) = false. Hãy xem cách thức chính thức hoá bên dưới để biết cách tổng quát hoá điều này cho nhiều đầu vào.
Cụ thể hơn, đối với mọi result_index trong index_space(results[0]):
adjusted_dimension = dimension >= 0 ? dimension : rank(inputs[0]) + dimension.result_slice = [ri0, ..., :, ..., riR-1]trong đóriNlà các phần tử riêng lẻ trongresult_indexvà:được chèn vàoadjusted_dimension.inputs_together = (inputs[0]..., ..., inputs[N-1]...).results_together[result_slice] = sort(inputs_together[result_slice], comparator_together).- trong đó
sortsắp xếp một lát cắt 1 chiều theo thứ tự không giảm, dự kiếncomparator_togethersẽ trả vềtruenếu đối số ở phía bên trái nhỏ hơn đối số thứ hai ở phía bên phải. def comparator_together(lhs_together, rhs_together): args = [] for (lhs_el, rhs_el) in zip(lhs_together, rhs_together): args.append(lhs_el) args.append(rhs_el) return comparator(*args)(results[0]..., ..., results[N-1]...) = results_together.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | inputs |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C1 – C5) |
| (I2) | dimension |
hằng số thuộc loại si64 |
(C4) |
| (I3) | is_stable |
hằng số thuộc loại i1 |
|
| (I4) | comparator |
hàm | (C5) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng tensor biến đổi hoặc tensor lượng tử hoá trên mỗi tensor | (C2), (C3) |
Giới hạn
- (C1)
0 < size(inputs). - (C2)
type(inputs...) = type(results...). - (C3)
same(shape(inputs...) + shape(results...)). - (C4)
-R <= dimension < R, trong đóR = rank(inputs[0]). - (C5)
comparatorcó loại(tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>, trong đóEi = element_type(inputs[i]).
Ví dụ
// %input0 = [[1, 2, 3], [3, 2, 1]]
// %input1 = [[3, 2, 1], [1, 2, 3]]
%result0, %result1 = "stablehlo.sort"(%input0, %input1) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>, %arg2: tensor<i64>, %arg3: tensor<i64>):
%predicate = "stablehlo.compare"(%arg0, %arg1) {
comparison_direction = #stablehlo<comparison_direction GT>
} : (tensor<i64>, tensor<i64>) -> tensor<i1>
"stablehlo.return"(%predicate) : (tensor<i1>) -> ()
}) {
dimension = 0 : i64,
is_stable = true
} : (tensor<2x3xi64>, tensor<2x3xi64>) -> (tensor<2x3xi64>, tensor<2x3xi64>)
// %result0 = [[3, 2, 3], [1, 2, 1]]
// %result1 = [[1, 2, 1], [3, 2, 3]]
sqrt
Ngữ nghĩa
Thực hiện phép toán căn bậc hai trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
squareRoottừ IEEE-754. - Đối với số phức: căn bậc hai của số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(sqrt, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [[0.0, 1.0], [4.0, 9.0]]
%result = "stablehlo.sqrt"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [2.0, 3.0]]
trừ
Ngữ nghĩa
Thực hiện phép trừ từng 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 số nguyên: phép trừ số nguyên.
- Đối với số thực:
subtractiontừ IEEE-754. - Đối với số phức: phép trừ số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(subtract, lhs, rhs, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
| (I2) | rhs |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại số nguyên, dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).
Ví dụ
// %lhs: [[6, 8], [10, 12]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.subtract"(%lhs, %rhs) : (tensor<2x2xf32>, tensor<2x2xf32>) -> (tensor<2x2xf32>)
// %result: [[1, 2], [3, 4]]
tan
Ngữ nghĩa
Thực hiện phép toán tiếp tuyến theo phần tử trên tensor operand và tạo ra tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
tantừ IEEE-754. - Đối với số phức: hàm tang số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(tan, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.tan"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [
// [0.0, 1.63312e+16],
// [0.0, 5.44375e+15]
// ]
tanh
Ngữ nghĩa
Thực hiện phép toán tan hyperbolic trên từng phần tử của tensor operand và tạo ra một tensor result. Tuỳ thuộc vào loại phần tử, hãy làm như sau:
- Đối với số thực:
tanhtừ IEEE-754. - Đối với số phức: tang hyperbol của số phức.
- Đối với các loại được lượng tử hoá:
dequantize_op_quantize(tanh, operand, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_type(operand) = baseline_type(result).
Ví dụ
// %operand: [-1.0, 0.0, 1.0]
%result = "stablehlo.tanh"(%operand) : (tensor<3xf32>) -> tensor<3xf32>
// %result: [-0.76159416, 0.0, 0.76159416]
chuyển vị
Ngữ nghĩa
Hoán vị các phương diện của tensor operand bằng cách sử dụng permutation và tạo ra một tensor result. Cụ thể hơn, result[result_index] = operand[operand_index]
trong đó result_index[d] = operand_index[permutation[d]].
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor hoặc tensor được lượng tử hoá | (C1-C4) |
| (I2) | permutation |
Hằng số tensor 1 chiều thuộc loại si64 |
(C2-C4) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor hoặc tensor được lượng tử hoá | (C1), (C3-C4) |
Giới hạn
- (C1)
element_type(result)được cho bởi:element_type(operand), nếu!is_per_axis_quantized(operand).element_type(operand), ngoại trừquantization_dimension(operand)vàquantization_dimension(result)có thể khác nhau.
- (C2)
permutationlà một 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 trên hoặc dưới.
Nói một cách chính thức hơn, cho 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ằng transpose_a, có thể là một trong những điều sau:
NO_TRANSPOSE: Thực hiện thao tác bằng cách sử dụnganguyên trạng.TRANSPOSE: Thực hiện thao tác trên chuyển vị củaa.ADJOINT: Thực hiện thao tác trên chuyể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 do quá trình triển khai xác định.
Nếu unit_diagonal là true, thì quá trình triển khai có thể giả định rằng các phần tử đường chéo của a bằng 1, nếu không, hành vi sẽ không xác định.
Đối với các loại được định lượng, hãy thực hiện dequantize_op_quantize(lambda x, y: triangular_solve(x, y, left_side, lower,
unit_diagonal, transpose_a), a, b, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | a |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C3) |
| (I2) | b |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1-C4) |
| (I3) | left_side |
hằng số thuộc loại i1 |
(C3) |
| (I4) | lower |
hằng số thuộc loại i1 |
|
| (I5) | unit_diagonal |
hằng số thuộc loại i1 |
|
| (I6) | transpose_a |
enum của NO_TRANSPOSE, TRANSPOSE và ADJOINT |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động hoặc số phức hoặc tensor lượng tử hoá trên mỗi tensor | (C1) |
Giới hạn
- (C1)
baseline_element_type(a) = baseline_element_type(b). - (C2)
2 <= rank(a) = rank(b) = R. - (C3) Mối quan hệ giữa
shape(a)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]
// ]
tuple
Ngữ nghĩa
Tạo một bộ result từ các giá trị val.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | val |
số lượng giá trị thay đổi | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tuple | (C1) |
Giới hạn
- (C1)
resultcó loạituple<E0, ..., EN-1>trong đóEi = type(val[i]).
Ví dụ
// %val0: memref[1.0, 2.0]
// %val1: (3)
%result = "stablehlo.tuple"(%val0, %val1) : (memref<2xf32>, tuple<tensor<i32>>) -> tuple<memref<2xf32>, tuple<tensor<i32>>>
// %result: (memref[1.0, 2.0], (3))
uniform_dequantize
Ngữ nghĩa
Thực hiện quá trình chuyển đổi từng phần tử của tensor được lượng tử hoá operand thành tensor dấu phẩy động result theo các tham số lượng tử hoá do loại operand xác định.
Nói một cách trang trọng hơn, result = dequantize(operand).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor được lượng tử hoá | (C1), (C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại dấu phẩy động | (C1), (C2) |
Giới hạn
- (C1)
shape(operand) = shape(result). - (C2)
element_type(result) = expressed_type(operand).
Ví dụ
// %operand: [10, 10]
%result = "stablehlo.uniform_dequantize"(%operand) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2xf32>
// %result: [4.0, 15.0]
uniform_quantize
Ngữ nghĩa
Thực hiện quá trình chuyển đổi từng phần tử của tensor dấu phẩy động hoặc tensor lượng tử hoá operand thành tensor lượng tử hoá result theo các tham số lượng tử hoá do loại result xác định.
Nói một cách trang trọng hơn,
- Nếu
is_float(operand):result = quantize(operand, type(result)).
- Nếu
is_quantized(operand):float_result = dequantize(operand).result = quantize(float_result, type(result)).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
tensor thuộc loại dấu phẩy động hoặc lượng tử hoá | (C1), (C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor được lượng tử hoá | (C1), (C2) |
Giới hạn
- (C1)
shape(operand) = shape(result). - (C2)
expressed_type(result) = is_float(operand) ? element_type(operand) : expressed_type(operand).
Ví dụ
// %operand: [4.0, 15.0]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2xf32>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>
// %result: [10, 10]
// %operand: [10, 10]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-20,0.2:-30}>>
// %result: [20, 45]
trong khi
Ngữ nghĩa
Tạo đầu ra từ việc thực thi hàm body 0 hoặc nhiều lần trong khi hàm cond xuất ra true. Nói một cách chính thức hơn, ngữ nghĩa có thể được biểu thị bằng cú pháp Python như sau:
internal_state = operand
while cond(*internal_state):
internal_state = body(*internal_state)
results = internal_state
Hành vi của vòng lặp vô hạn sẽ được xác định sau (#383).
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | operand |
số lượng giá trị thay đổi | (C1-C3) |
| (I2) | cond |
hàm | (C1) |
| (I3) | body |
hàm | (C2) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
results |
số lượng giá trị thay đổi | (C3) |
Giới hạn
- (C1)
condcó kiểu(T0, ..., TN-1) -> tensor<i1>, trong đóTi = type(operand[i]). - (C2)
bodycó loại(T0, ..., TN-1) -> (T0, ..., TN-1), trong đóTi = type(operand[i]). - (C3)
type(results...) = type(operand...).
Ví dụ
// %init_i: 1
// %init_sum: 0
// %one: 1
// %ten: 10
%results0, %results1 = "stablehlo.while"(%init_i, %init_sum) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%cond = "stablehlo.compare"(%arg0, %ten) {
comparison_direction = #stablehlo<comparison_direction LT>
} : (tensor<i64>, tensor<i64>) -> tensor<i1>
stablehlo.return %cond : tensor<i1>
}, {
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%new_sum = stablehlo.add %arg1, %one : tensor<i64>
%new_i = stablehlo.add %arg0, %one : tensor<i64>
stablehlo.return %new_i, %new_sum : tensor<i64>, tensor<i64>
}) : (tensor<i64>, tensor<i64>) -> (tensor<i64>, tensor<i64>)
// %results0: 10
// %results1: 10
xor
Ngữ nghĩa
Thực hiện phép toán 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 làm như sau:
- Đối với giá trị boolean: phép XOR logic.
- Đối với số nguyên: đảo bit XOR.
Thông tin đầu vào
| Hãng nhạc | Tên | Loại | Giới hạn |
|---|---|---|---|
| (I1) | lhs |
tensor thuộc loại boolean hoặc số nguyên | (C1) |
| (I2) | rhs |
tensor thuộc loại boolean hoặc số nguyên | (C1) |
Kết quả đầu ra
| Tên | Loại | Giới hạn |
|---|---|---|
result |
tensor thuộc loại boolean hoặc số nguyên | (C1) |
Giới hạn
- (C1)
type(lhs) = type(rhs) = type(result).
Ví dụ
// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.xor"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[4, 4], [4, 12]]
// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.xor"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, false]]
Khả năng tương tác giữa các phương ngữ
Hiện tại, các chương trình StableHLO đôi khi chứa các thao tác không được StableHLO xác định.
Mô-đun, Hàm, Lệnh gọi và Trả về
StableHLO sử dụng các thao tác MLIR ở thượng nguồn cho ModuleOp, FuncOp, CallOp và ReturnOp. Điều này được thực hiện để có khả năng tương tác tốt hơn với cơ chế MLIR hiện có, vì nhiều lượt truyền hữu ích được viết nhắm đến FuncOp và ModuleOp, đồng thời nhiều quy trình biên dịch dự kiến sẽ có các thao tác này. Đảm bảo khả năng tương thích đầy đủ được áp dụng cho các thao tác này. Nếu có bất kỳ thay đổi nào về các thao tác này theo cách không tương thích (tức là xoá), thì các thao tác tương đương StableHLO sẽ được thêm vào để duy trì khả năng tương thích.
CHLO
Opset CHLO chứa các thao tác cấp cao hơn phân tách thành StableHLO. Hiện tại, không có gì đảm bảo về khả năng tương thích cho CHLO. Để đảm bảo khả năng tương thích, bạn phải sử dụng chlo-legalize-to-stablehlo pass trước khi chuyển đổi tuần tự.
Thao tác với hình dạng
Đây là một trường hợp sử dụng phổ biến trong cộng đồng để sử dụng một số thao tác từ các phương ngữ MLIR cốt lõi trong các chương trình StableHLO động nhằm thực hiện các phép tính hình dạng.
Phổ biến nhất là các thao tác shape phương ngữ như shape_of hoặc num_elements, các thao tác tensor phương ngữ như dim hoặc from_elements và loại index tích hợp.
Dynamism RFC > O2 cho biết những loại này nằm ngoài phạm vi, tuy nhiên, một số loại index được hỗ trợ cho mục đích tương tác. Không có gì đảm bảo về khả năng tương thích cho các thao tác hoặc loại này. Bạn có thể dùng lệnh truyền shape-legalize-to-stablehlo để chuyển đổi các thao tác này thành các thao tác StableHLO được hỗ trợ đầy đủ.
Các thao tác không dùng nữa
Có một số thao tác StableHLO được kế thừa từ MHLO, những thao tác này không còn được dùng nữa và sắp bị loại bỏ khỏi StableHLO. Bạn có thể xem toàn bộ thông tin chi tiết về những lần xoá này trong StableHLO v1.0 Cleanup #2283. Vấn đề trên trình theo dõi cho những hoạt động không dùng nữa này là #2340.
Các thao tác này thuộc một số danh mục:
- Danh mục "Không có trong HLO" của các hoạt động StableHLO – ban đầu, các hoạt động này thuộc opset StableHLO nhưng sau đó được coi là không phù hợp với opset này:
broadcast,create_token,cross-replica-sum,dot,einsum,torch_index_select,unary_einsum(#3). - Các thao tác không dùng đến – Những thao tác này có thể hữu ích tại một thời điểm nào đó, nhưng các thao tác này chưa được phát triển đầy đủ hoặc các quy trình sử dụng những thao tác này đã được tái cấu trúc để không cần đến chúng nữa. Các API này bao gồm
map,tuple(#598),get_tuple_element,rng,complexso sánh #560 và tích chậpwindow_reversal(#1181).
Một số thao tác trong số này có thể dễ dàng bị xoá vì chúng có thể được biểu thị bằng các thao tác hiện có (broadcast, create_token, cross-replica-sum, dot, unary_einsum) và sẽ bị xoá sau khi hết thời gian tương thích hiện tại (6 tháng). Các thao tác khác vẫn đang được xem xét để loại bỏ (einsum, get_tuple_element, map, rng torch_index_select, tuple, complex so sánh, window_reversal). Tuỳ thuộc vào ý kiến phản hồi của cộng đồng, các thao tác này sẽ bị loại bỏ hoặc được thêm vào quy cách với sự hỗ trợ đầy đủ. Cho đến khi biết được các hoạt động này trong tương lai, chúng chỉ được đảm bảo khả năng tương thích trong 6 tháng.
Thực thi
Thực thi tuần tự
Một chương trình StableHLO được thực thi bằng cách cung cấp các giá trị đầu vào cho hàm main và tính toán các giá trị đầu ra. Các giá trị đầu ra của một hàm được tính bằng cách thực thi biểu đồ của các thao tác bắt nguồn từ thao tác return tương ứng.
Thứ tự thực thi được xác định theo cách triển khai miễn là thứ tự đó phù hợp với luồng dữ liệu, tức là nếu các thao tác được thực thi trước khi sử dụng. Trong StableHLO, tất cả các hoạt động có tác dụng phụ đều sử dụng một mã thông báo và tạo ra một mã thông báo (nhiều mã thông báo có thể được ghép kênh thành một mã thông báo thông qua after_all), do đó, thứ tự thực thi của các tác dụng phụ cũng được điều chỉnh theo luồng dữ liệu. Ví dụ: trong chương trình bên dưới, có 2 thứ tự thực thi có thể xảy ra: %0 → %1 → %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>
}
Cụ thể hơn, quy trình StableHLO là sự kết hợp của:
1) một chương trình StableHLO, 2) trạng thái hoạt động (chưa thực thi, đã thực thi) và 3) các giá trị trung gian mà quy trình đang xử lý.
Quy trình này bắt đầu bằng các giá trị đầu vào cho hàm main, tiến hành thông qua biểu đồ của các thao tác cập nhật trạng thái hoạt động và các giá trị trung gian, đồng thời kết thúc bằng các giá trị đầu ra. Việc chính thức hoá thêm sẽ được xác định sau (#484).
Thực thi song song
Các chương trình StableHLO có thể được thực thi song song, được sắp xếp thành một lưới quy trình 2D gồm num_replicas theo num_partitions, cả hai đều có loại ui32.
Trong lưới quy trình StableHLO, num_replicas * num_partitions quy trình StableHLO đang thực thi cùng một lúc. Mỗi quy trình có một process_id = (replica_id, partition_id) duy nhất, trong đó replica_id trong replica_ids = range(num_replicas) 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 kích thước này vào một phần rõ ràng của các chương trình StableHLO #650), và vị trí trong lưới quy trình được biết một cách tĩnh cho mọi quy trình. Mỗi quy trình có quyền truy cập vào vị trí của quy trình đó trong lưới quy trình thông qua các thao tác replica_id và partition_id.
Trong lưới quy trình, các chương trình có thể giống nhau (theo kiểu "Một chương trình, nhiều dữ liệu"), có thể khác nhau (theo kiểu "Nhiều chương trình, nhiều dữ liệu") hoặc nằm ở giữa. Trong tương lai, chúng tôi dự định sẽ hỗ trợ các thành phần khác để xác định các chương trình StableHLO song song, bao gồm cả GSPMD (#619).
Trong lưới quy trình, các quy trình hầu hết độc lập với nhau – chúng có trạng thái hoạt động riêng, các giá trị đầu vào/trung gian/đầu ra riêng và hầu hết các thao tác đều được thực thi riêng giữa các quy trình, ngoại trừ một số ít thao tác tập thể được mô tả bên dưới.
Vì việc thực thi hầu hết các thao tác chỉ sử dụng các giá trị từ cùng một quy trình, nên thường không có gì mơ hồ khi tham chiếu đến các giá trị này theo tên của chúng.
Tuy nhiên, khi mô tả ngữ nghĩa của các thao tác tập thể, điều đó là không đủ và điều đó dẫn đến ký hiệu name@process_id để chỉ giá trị name trong một quy trình cụ thể. (Theo quan điểm đó, name không đủ tiêu chuẩn có thể được xem là cách viết tắt của name@(replica_id(), partition_id())).
Thứ tự thực thi trên các quy trình được xác định theo cách triển khai, ngoại trừ quá trình đồng bộ hoá do giao tiếp điểm-điểm và các thao tác tập thể được giới thiệu như mô tả bên dưới.
Giao tiếp điểm-điểm
Các quy trình StableHLO có thể giao tiếp với nhau thông qua các kênh StableHLO. Kênh được biểu thị bằng một mã nhận dạng dương thuộc loại si64. Thông qua nhiều thao tác, bạn có thể gửi giá trị đến các kênh và nhận giá trị từ các kênh.
Việc chính thức hoá thêm, chẳng hạn như các mã nhận dạng kênh này đến từ đâu, cách các chương trình xử lý nhận biết chúng và loại đồng bộ hoá nào được chúng giới thiệu, sẽ được xác định sau (#484).
Truyền thông trực tuyến
Mọi quy trình StableHLO đều có quyền truy cập vào 2 giao diện truyền phát trực tuyến:
- Infeed có thể đọc được.
- Outfeed có thể được ghi vào.
Không giống như các kênh được dùng để giao tiếp giữa các quy trình và do đó có các quy trình ở cả hai đầu, infeeds và outfeeds có việc triển khai đầu kia do người dùng xác định.
Việc chính thức hoá thêm, chẳng hạn như cách giao tiếp truyền trực tuyến ảnh hưởng đến thứ tự thực thi và loại đồng bộ hoá nào được giới thiệu bởi giao tiếp truyền trực tuyến, sẽ được xác định sau (#484).
Collective ops
Có 6 thao tác tập thể trong StableHLO: all_gather, all_reduce, all_to_all, collective_broadcast, collective_permute và reduce_scatter. Tất cả các thao tác này đều chia các quy trình trong lưới quy trình StableHLO thành các nhóm quy trình StableHLO và thực thi một phép tính chung trong mỗi nhóm quy trình, độc lập với các nhóm quy trình khác.
Trong mỗi nhóm quy trình, các thao tác tập thể có thể giới thiệu một rào cản đồng bộ hoá. Việc chính thức hoá thêm, chẳng hạn như giải thích rõ thời điểm chính xác diễn ra quá trình đồng bộ hoá này, cách chính xác mà các quy trình đạt đến rào cản này và điều gì sẽ xảy ra nếu chúng không đạt được, sẽ được xác định sau (#484).
Nếu nhóm quy trình liên quan đến giao tiếp giữa các phân vùng, tức là có các quy trình trong nhóm quy trình có mã phân vùng khác nhau, thì việc thực thi thao tác tập thể cần một kênh và thao tác tập thể phải cung cấp một channel_id dương thuộc loại si64. Giao tiếp giữa các bản sao không cần đến các kênh.
Các phép tính do các thao tác tập thể thực hiện là dành riêng cho từng thao tác và được mô tả trong các phần thao tác riêng lẻ ở trên. Tuy nhiên, các chiến lược mà lưới quy trình được chia thành các nhóm quy trình sẽ được chia sẻ giữa các thao tác này và được mô tả trong phần này. Cụ thể hơn, StableHLO hỗ trợ 4 chiến lược sau.
cross_replica
Chỉ có hoạt động giao tiếp giữa các bản sao diễn ra trong mỗi nhóm quy trình. Chiến lược này lấy replica_groups – danh sách các danh sách mã nhận dạng bản sao – và tính tích Đề các của replica_groups theo partition_ids. replica_groups phải có các phần tử riêng biệt và bao gồm tất cả replica_ids. Cụ thể hơn, sử dụng cú pháp Python:
def cross_replica(replica_groups: List[List[ReplicaId]]) -> List[List[ProcessId]]:
for replica_group in replica_groups:
for partition_id in partition_ids:
process_group = []
for replica_id in replica_group:
process_group.append((replica_id, partition_id))
yield process_group
Ví dụ: đối với replica_groups = [[0, 1], [2, 3]] 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 diễn ra trong mỗi nhóm quy trình. Chiến lược này lấy partition_groups – danh sách các danh sách mã nhận dạng phân vùng – và tính tích Đề các của partition_groups theo replica_ids.
partition_groups phải có các phần tử duy nhất và bao gồm tất cả partition_ids.
Cụ thể hơn, sử dụng cú pháp Python:
def cross_partition(partition_groups: List[List[PartitionId]]) -> List[List[ProcessId]]:
for partition_group in partition_groups:
for replica_id in replica_ids:
process_group = []
for partition_id in partition_group:
process_group.append((replica_id, partition_id))
yield process_group
Ví dụ: đối với partition_groups = [[0, 1]] 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à giữa các phân vùng đều có thể diễn ra trong mỗi nhóm quy trình. Chiến lược này lấy replica_groups – danh sách các danh sách mã nhận dạng bản sao – và tính tích Đề các của mỗi replica_group theo partition_ids. replica_groups phải có các phần tử duy nhất và bao gồm tất cả replica_ids. Cụ thể hơn, sử dụng cú pháp Python:
def cross_replica_and_partition(replica_groups: List[List[ReplicaId]]) -> List[List[ProcessId]]:
for replica_group in replica_groups:
process_group = []
for partition_id in partition_ids:
for replica_id in replica_group:
process_group.append((replica_id, partition_id))
yield process_group
Ví dụ: đối với replica_groups = [[0, 1], [2, 3]] 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 "được làm phẳng" ở dạng replica_id * num_partitions + partition_id – và chuyển chúng thành mã nhận dạng quy trình. flattened_id_groups phải có các phần tử riêng biệt và bao gồm tất cả process_ids. Cụ thể hơn, sử dụng cú pháp Python:
def flattened_ids(flattened_id_groups: List[List[ui32]]) -> List[List[ProcessId]]:
for flattened_id_group in flattened_id_groups:
process_group = []
for flattened_id in flattened_id_group:
replica_id = flattened_id // num_partitions
partition_id = flattened_id % num_partitions
process_group.append((replica_id, partition_id))
yield process_group
Ví dụ: đối với flattened_id_groups = [[0, 1, 2, 3], [4, 5, 6, 7]], num_replicas = 4 và num_partitions = 2, flattened_ids sẽ tạo ra [[(0, 0), (0, 1), (1, 0), (1, 1)], [(2, 0), (2, 1), (3, 0), (3, 1)]].
Độ chính xác
Hiện tại, StableHLO không đảm bảo độ chính xác về số học, nhưng điều này có thể thay đổi trong tương lai (#1156).
Ngữ nghĩa thực thi của phép toán được lượng tử hoá
Việc diễn giải các hoạt động StableHLO được lượng tử hoá có thể khác nhau tuỳ thuộc vào các yêu cầu và khả năng của phần cứng. Ví dụ: một số phần cứng có thể chọn diễn giải các hoạt động được lượng tử hoá bằng cách sử dụng chiến lược "giảm lượng tử hoá, thực hiện hoạt động dấu phẩy động và cuối cùng là lượng tử hoá". Những người khác có thể thực hiện toàn bộ quá trình tính toán bằng số học số nguyên. Do đó, việc diễn giải các hoạt động StableHLO được lượng tử hoá chỉ do việc triển khai cụ thể xác định. Việc diễn giải lượng tử hoá kết hợp (#1575) phải dựa trên ngữ nghĩa của nó như quy định trong thông số kỹ thuật (thông qua 1792).
Lỗi
Các chương trình StableHLO được xác thực thông qua một tập hợp lớn các ràng buộc cho từng thao tác, giúp loại bỏ nhiều loại lỗi trước thời gian chạy. Tuy nhiên, vẫn có thể xảy ra các điều kiện lỗi, chẳng hạn như do tràn số nguyên, truy cập ngoài phạm vi, v.v. Trừ phi được gọi ra một cách rõ ràng, tất cả các lỗi này đều dẫn đến hành vi do quá trình triển khai xác định, nhưng điều này có thể thay đổi trong tương lai (#1157).
Ngoại lệ dấu phẩy động
Theo ngoại lệ đối với quy tắc này, các ngoại lệ dấu phẩy động trong chương trình StableHLO có hành vi được xác định rõ. Các thao tác dẫn đến các trường hợp ngoại lệ do tiêu chuẩn IEEE-754 xác định (thao tác không hợp lệ, phép chia cho 0, tràn số, tràn số âm hoặc trường hợp ngoại lệ không chính xác) sẽ tạo ra kết quả mặc định (như được xác định trong tiêu chuẩn) và tiếp tục thực thi mà không làm tăng cờ trạng thái tương ứng; tương tự như xử lý ngoại lệ raiseNoFlag theo tiêu chuẩn. Các trường hợp ngoại lệ đối với các thao tác không theo tiêu chuẩn (ví dụ: số học phức tạp và một số hàm siêu việt nhất định) được xác định theo cách triển khai.
Hình dạng không khớp
StableHLO hỗ trợ các tensor có hình dạng động. Tuy nhiên, các hình dạng phải đồng ý tại thời gian chạy, nếu không, hành vi sẽ không xác định. StableHLO không cung cấp một thao tác rõ ràng có thể khẳng định rằng một tensor có một hình dạng nhất định tại thời gian chạy. Nhà sản xuất có trách nhiệm tạo mã chính xác.
Ví dụ cụ thể: chương trình dưới đây là hợp lệ. Tuy nhiên, trong thời gian chạy, các hình dạng chính xác của %arg0 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 đang sử dụng cú pháp EBNF theo ISO đã sửa đổi (ISO/IEC 14977:1996, Wikipedia), với 2 điểm sửa đổi: 1) các quy tắc được xác định bằng cách sử dụng ::= thay vì =,
2) phép nối được biểu thị bằng cách đặt cạnh nhau thay vì ,.
Để mô tả ngữ nghĩa (tức là trong các phần "Types" (Kiểu dữ liệu), "Constants" (Hằng số) và "Ops" (Thao tác)), chúng tôi đang sử dụng các công thức dựa trên cú pháp Python được mở rộng để hỗ trợ việc biểu thị ngắn gọn các thao tác trên mảng như mô tả bên dưới. Điều này phù hợp với các đoạn mã nhỏ, nhưng trong một số ít trường hợp cần các đoạn mã lớn hơn, chúng tôi sử dụng cú pháp Python thuần tuý và luôn giới thiệu rõ ràng.
Công thức
Hãy khám phá cách hoạt động của công thức dựa trên ví dụ trong quy cách dot_general. Một trong các ràng buộc cho thao tác này có dạng như sau:dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...).
Tên được dùng trong công thức này đến từ 2 nguồn: 1) hàm chung, tức là dim, 2) định nghĩa thành phần của phần tử chương trình tương ứng, tức là lhs, lhs_batching_dimensions, rhs và rhs_batching_dimensions đầu vào được xác định trong phần "Đầu vào" của dot_general.
Như đã đề cập ở trên, cú pháp của công thức này dựa trên Python với một số tiện ích hướng đến sự súc tích. Để hiểu rõ công thức này, hãy chuyển đổi công thức đó thành cú pháp Python thông thường.
A) Trong các công thức này, chúng ta đang sử dụng = để biểu thị sự bình đẳng, vì vậy, bước đầu tiên để có được cú pháp Python là thay thế = bằng ==, như sau: dim(lhs, lhs_batching_dimensions...) == dim(rhs, rhs_batching_dimensions...).
B) Ngoài ra, các công thức này hỗ trợ dấu ba chấm (...) để chuyển biểu thức vô hướng thành biểu thức tensor. Nói tóm lại, f(xs...) có nghĩa là "đối với mỗi đại lượng vô hướng x trong tensor xs, hãy tính toán một đại lượng vô hướng f(x) rồi trả về tất cả các kết quả vô hướng này cùng nhau dưới dạng kết quả tensor". Trong cú pháp Python thông thường, công thức ví dụ của chúng ta sẽ chuyển thành: [dim(lhs, dim1) for dim1 in lhs_batching_dimensions] ==
[dim(rhs, dim2) for dim2 in rhs_batching_dimensions].
Nhờ dấu ba chấm, bạn thường có thể tránh làm việc ở cấp độ của các đại lượng vô hướng riêng lẻ. Tuy nhiên, trong một số trường hợp phức tạp, bạn có thể sử dụng cú pháp bán chính thức ở cấp thấp hơn, chẳng hạn như trong công thức start_indices[bi0, ..., :, ..., biN] trong quy cách gather. Để đảm bảo tính súc tích, chúng tôi không cung cấp một hình thức chính thức chính xác để dịch cú pháp như vậy sang Python thuần tuý, với hy vọng rằng bạn vẫn có thể hiểu một cách trực quan theo từng trường hợp.
Vui lòng cho chúng tôi biết nếu bạn thấy một số công thức cụ thể khó hiểu, chúng tôi sẽ cố gắng cải thiện những công thức đó.
Ngoài ra, bạn sẽ nhận thấy rằng các công thức sử dụng dấu ba chấm để mở rộng mọi loại danh sách, bao gồm cả tensor, danh sách tensor (chẳng hạn như có thể phát sinh từ số lượng tensor có thể thay đổi), v.v. Đây là một lĩnh vực khác mà chúng tôi không cung cấp một hình thức chính thức chính xác (ví dụ: danh sách thậm chí không phải là một phần của hệ thống loại StableHLO) và thay vào đó dựa vào khả năng hiểu trực quan.
C) Phương tiện ký hiệu đáng chú ý cuối cùng mà chúng ta sử dụng là truyền tin ngầm. Mặc dù opset StableHLO không hỗ trợ truyền tin ngầm, nhưng các công thức thì có, cũng nhằm mục đích ngắn gọn. Nói tóm lại, nếu một đại lượng vô hướng được dùng trong một ngữ cảnh mà một tensor được dự kiến, thì đại lượng vô hướng sẽ được truyền đến hình dạng dự kiến.
Để tiếp tục ví dụ về dot_general, sau đây là một ràng buộc khác: 0 <= lhs_batching_dimensions < rank(lhs). Như được xác định trong quy cách dot_general, lhs_batching_dimensions là một tensor, tuy nhiên cả 0 và rank(lhs) đều là các đại lượng vô hướng. Sau khi chúng ta áp dụng tính năng truyền tin ngầm, công thức sẽ trở thành [0, ..., 0] <= lhs_batching_dimensions < [rank(lhs), ..., rank(lhs)].
Khi áp dụng cho một thao tác dot_general cụ thể, công thức này sẽ đánh giá thành một tensor gồm các giá trị boolean. Khi công thức được dùng làm điều kiện ràng buộc, điều kiện ràng buộc sẽ giữ nguyên nếu công thức đánh giá thành true hoặc thành một tensor chỉ có các phần tử true.
Tên
Trong công thức, phạm vi từ vựng bao gồm: 1) hàm chung, 2) định nghĩa thành viên,
3) định nghĩa cục bộ. Danh sách các hàm chung được cung cấp bên dưới. Danh sách định nghĩa về phần tử phụ thuộc vào phần tử chương trình mà ký hiệu được áp dụng:
- Đối với các thao tác, định nghĩa thành phần bao gồm tên được giới thiệu trong các phần "Đầu vào" và "Đầu ra".
- Đối với mọi thứ khác, định nghĩa thành phần bao gồm các phần cấu trúc của phần tử chương trình, được đặt tên theo các ký tự không kết thúc EBNF tương ứng. Hầu hết thời gian, tên của các phần cấu trúc này được lấy bằng cách chuyển đổi tên của các phần tử không kết thúc thành snake case (ví dụ:
IntegerLiteral=>integer_literal), nhưng đôi khi tên sẽ được rút gọn trong quá trình này (ví dụ:QuantizationStorageType=>storage_type). Trong trường hợp đó, tên sẽ được giới thiệu một cách rõ ràng tương tự như các phần "Đầu vào" / "Đầu ra" trong quy cách hoạt động. - Ngoài ra, định nghĩa về thành viên luôn bao gồm
selfđể tham chiếu đến phần tử chương trình tương ứng.
Giá trị
Khi được đánh giá, các công thức sẽ hoạt động với những loại giá trị sau:
1) Value (giá trị thực tế, ví dụ: dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>; các giá trị này luôn biết loại của chúng),
2) Placeholder (giá trị trong tương lai, ví dụ: lhs, rhs hoặc result; giá trị thực tế của các giá trị này chưa được biết, chỉ biết loại của chúng),
3) Type (các loại như được xác định trong phần "Loại"),
4) Function (các hàm chung như được xác định trong phần "Hàm").
Tuỳ thuộc vào ngữ cảnh, tên có thể đề cập đến các giá trị khác nhau. Cụ thể hơn, phần "Ngữ nghĩa" cho các thao tác (và các phần tương đương cho các phần tử chương trình khác) xác định logic thời gian chạy, vì vậy, tất cả các đầu vào đều có sẵn dưới dạng Value.
Ngược lại, phần "Constraints" (Ràng buộc) cho các thao tác (và các thao tác tương đương) xác định logic "thời gian biên dịch", tức là một thứ thường được thực thi trước thời gian chạy, vì vậy, chỉ các đầu vào hằng số mới có sẵn dưới dạng Value và các đầu vào khác chỉ có sẵn dưới dạng Placeholder.
| Tên | Trong "Ngữ nghĩa" | Trong phần "Constraints" (Ràng buộc) |
|---|---|---|
| Hàm chung | Function |
Function |
| Đầu vào hằng số | Value |
Value |
| Đầu vào không phải hằng số | Value |
Placeholder |
| Kết quả đầu ra | Value |
Placeholder |
| Định nghĩa về địa phương | Tuỳ thuộc vào định nghĩa | Tuỳ thuộc vào định nghĩa |
Hãy xem xét một ví dụ về thao tác transpose:
%result = "stablehlo.transpose"(%operand) {
permutation = dense<[2, 1, 0]> : tensor<3xi64>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>
Đối với thao tác này, permutation là một hằng số, vì vậy, hằng số này có sẵn dưới dạng Value trong cả ngữ nghĩa và các ràng buộc. Ngược lại, operand và result có sẵn dưới dạng Value trong ngữ nghĩa nhưng chỉ dưới dạng Placeholder trong các ràng buộc.
Hàm
Xây dựng các loại
Không có hàm nào có thể dùng để tạo các loại. Thay vào đó, chúng ta sử dụng trực tiếp cú pháp kiểu vì cú pháp này thường ngắn gọn hơn. Ví dụ: (tensor<E>, tensor<E>) -> (tensor<E>) thay vì function_type(
[tensor_type([], E), tensor_type([], E)], [tensor_type([], E)]).
Hàm trên các loại
element_typeđược xác định trên các loại tensor và các loại tensor được lượng tử hoá, đồng thời trả về phầnTensorElementTypehoặcQuantizedTensorElementTypecủaTensorTypehoặcQuantizedTensorTypetương ứng.
def element_type(x: Value | Placeholder | Type):
if type(x) == TensorType:
return tensor_element_type(x)
if type(x) == QuantizedTensorType:
return quantized_tensor_element_type(x)
if type(x) is not Type:
return element_type(type(x))
is_per_axis_quantized(x: Value | Placeholder | Type) -> Valuelà lối tắt chois_quantized(x) and quantization_dimension(x) is not None.is_per_tensor_quantized(x: Value | Placeholder | Type) -> Valuelà một lối tắt chois_quantized(x) and quantization_dimension(x) is None.is_promotable(x: Type, y: Type) -> boolkiểm tra xem kiểuxcó thể được chuyển đổi thành kiểuyhay không. KhixvàylàQuantizedTensorElementType, chương trình khuyến mãi chỉ được áp dụng chostorage_type. Phiên bản khuyến mãi cụ thể này hiện được dùng trong bối cảnh tính toán mức giảm (tham khảo RFC để biết thêm chi tiết).
def is_promotable(x: Type, y: Type) -> Value:
is_same_type = (is_bool(x) and is_bool(y)) or
(is_integer(x) and is_integer(y)) or (is_float(x) and is_float(y)) or
(is_complex(x) and is_complex(y)) or
(is_quantized(x) and is_quantized(y) and expressed_type(x) = expressed_type(y))
if is_same_type == False:
return False
if is_integer(x) or is_float(x):
return bitwidth(x) <= bitwidth(y)
if is_complex(x):
return bitwidth(element_type(x)) <= bitwidth(element_type(y))
if is_quantized(x):
return bitwidth(storage_type(x)) <= bitwidth(storage_type(y))
return false
is_quantized(x: Value | Placeholder | Type) -> Valuelà lối tắt chois_quantized_tensor_element_type(x).is_type_name(x: Value | Placeholder | Type) -> Value. Dùng được cho tất cả các loại. Ví dụ:is_float(x)sẽ trả vềtruenếuxlàFloatType. Nếuxlà một giá trị hoặc phần giữ chỗ, thì hàm này là lối tắt chois_type_name(type(x)).max_value(x: Type) -> Valuetrả về giá trị tối đa củaTensorElementType. Nếuxkhông phải làTensorElementType, thì sẽ trả vềNone.min_value(x: Type) -> Valuetrả về giá trị tối thiểu có thể có củaTensorElementType. Nếuxkhông phải làTensorElementType, thì sẽ trả vềNone.member_name(x: Value | Placeholder | Type) -> Any. Áp dụng cho tất cả các định nghĩa thành viênmember_namethuộc mọi loại. Ví dụ:tensor_element_type(x)trả về phầnTensorElementTypecủaTensorTypetương ứng. Nếuxlà một giá trị hoặc phần giữ chỗ, thì hàm này là lối tắt chomember_name(type(x)). Nếuxkhông phải là một loại có thành phần thích hợp hoặc một giá trị hoặc phần giữ chỗ của loại đó, thì sẽ trả vềNone.is_empty_algorithm(*args: Type)kiểm tra xem tất cả các trường thuật toán dấu chấm có được đặt thànhNonehay không. Điều này là cần thiết vì các thuật toán dấu chấm có hành vi mặc định do quá trình triển khai xác định, nên việc chỉ định giá trị mặc định sẽ không chính xác.
Xây dựng các giá trị
operation_name(*xs: Value | Type) -> Value. Dành cho tất cả các hoạt động. Ví dụ:add(lhs, rhs)lấy hai giá trị tensorlhsvàrhs, đồng thời trả về kết quả đánh giá thao tácaddvới các giá trị đầu vào này. Đối với một số thao tác, ví dụ:broadcast_in_dim, các loại đầu ra của chúng là "chịu tải", tức là cần thiết để đánh giá một thao tác. Trong trường hợp này, hàm sẽ lấy các loại này làm đối số.
Hàm trên các giá trị
Bạn có thể sử dụng tất cả các toán tử và hàm của Python. Ví dụ: cả ký hiệu subscription và slicing của Python đều có sẵn để lập chỉ mục vào các tensor, tensor được lượng tử hoá và các bộ dữ liệu.
to_destination_type(x: Value, destination_type: Type) -> Valueđược xác định trên các tensor và trả về giá trị đã chuyển đổi củaxdựa trêntype(x)vàdestination_typenhư sau:
def to_destination_type(x: Value, destination_type: Type) -> Value:
if type(x) == destination_type:
return x
if is_quantized(destination_type):
if is_quantized(type(x)):
return quantize(x, destination_type)
assert is_float(type(x))
return quantize(x, destination_type)
if is_quantized(type(x)):
assert destination_type = expressed_type(type(x))
return dequantize(type(x))
return convert(x, destination_type)
Có 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 các tensor và trả vềtruenếu tất cả các phần tử củaxlàNaNhoặcfalse. Nếuxkhông phải là một tensor, thì hàm này sẽ trả vềNone.is_sorted(x: Value) -> Valueđược xác định trên các tensor và trả vềtruenếu các phần tử củaxđược sắp xếp theo thứ tự tăng dần đối với thứ tự từ vựng tăng dần của chỉ mục hoặcfalsecủa chúng. Nếuxkhông phải là một tensor, hàm sẽ trả vềNone.is_unique(x: Value) -> Valueđược xác định trên các tensor và trả vềtruenếuxkhông có các phần tử trùng lặp hoặcfalsenếu không. Nếuxkhông phải là một tensor, thì hàm này sẽ trả vềNone.member_name(x: Value) -> Anyđược xác định cho tất cả các định nghĩa thành viênmember_namecủa tất cả các giá trị. Ví dụ:real_part(x)trả về phầnRealPartcủaComplexConstanttương ứng. Nếuxkhông phải là giá trị có thành phần thích hợp, hàm sẽ trả vềNone.same(x: Value) -> Valueđược xác định trên các tensor và trả vềtruenếu các phần tử củaxđều bằng nhau hoặcfalsenếu không. Nếu tensor không có phần tử, thì điều đó được tính là "tất cả đều bằng nhau", tức là hàm trả vềtrue. Nếuxkhông phải là một tensor, thì hàm này sẽ trả vềNone.split(x: Value, num_results: Value, axis: Value) -> Valueđược xác định trên các tensor và trả về các látnum_resultscủaxdọc theo trụcaxis. Nếuxkhông phải là một tensor hoặcdim(x, axis) % num_results != 0, thì hàm này sẽ trả vềNone.is_defined_in_parent_scope(x: Value) -> Valueđược xác định trên các chuỗi và trả vềtruenếuxlà tên của một hàm được xác định trong cùng phạm vi với hàm mẹ của thao tác có liên quan.is_namespaced_op_name(x: Value) -> Valueđược xác định trên các chuỗi và trả vềtruenếuxlà tên thao tác hợp lệ, tức là tên đó tuân theo biểu thức chính quy sau:[a-zA-Z][a-zA-Z0-9_]*([.][a-zA-Z0-9_$]+)+
Tính toán hình dạng
axes(x: Value | Placeholder | Type) -> Valuelà lối tắt chorange(rank(x)).dim(x: Value | Placeholder | Type, axis: Value) -> Valuelà lối tắt choshape(x)[axis].dims(x: Value | Placeholder | Type, axes: List) -> Listlà lối tắt cholist(map(lambda axis: dim(x, axis), axes)).index_space(x: Value | Placeholder | Type) -> Valueđược xác định trên các tensor và trả về chỉ mụcsize(x)choTensorTypetương ứng được sắp xếp theo thứ tự từ điển tăng dần, tức là[0, ..., 0],[0, ..., 1], ...,shape(x) - 1. Nếuxkhông phải là một loại tensor, loại tensor được lượng tử hoá hoặc một giá trị hay một phần giữ chỗ của một trong các loại này, thì hàm sẽ trả vềNone.rank(x: Value | Placeholder | Type) -> Valuelà lối tắt chosize(shape(x)).shape(x: Value | Placeholder | Type) -> Valueđược xác định trong phần "Hàm trên các loại" thông quamember_name.size(x: Value | Placeholder | Type) -> Valuelà lối tắt choreduce(lambda x, y: x * y, shape(x)).
Phép tính lượng tử hoá
def baseline_element_type(x: Value | Placeholder | Type) -> Typelà một lối tắt choelement_type(baseline_type(x)).baseline_typeđược xác định trên các loại tensor và các loại tensor được lượng tử hoá, đồng thời chuyển đổi chúng thành một "đường cơ sở", tức là một loại có cùng hình dạng nhưng các thông số lượng tử hoá của loại phần tử được đặt lại thành các giá trị mặc định. Đây là một thủ thuật hữu ích để so sánh cả hai loại tensor và tensor được định lượng một cách đồng nhất, điều này thường cần thiết. Đối với các loại được lượng tử hoá, điều này cho phép so sánh các loại bỏ qua các tham số lượng tử hoá, tức làshape,storage_type,expressed_type,storage_min,storage_maxvàquantization_dimension(đối với loại được lượng tử hoá theo trục) phải khớp với nhau, nhưngscalesvàzero pointscó thể khác nhau.
def baseline_type(x: Value | Placeholder | Type) -> Type:
if type(x) == TensorType:
return x
if type(x) == QuantizedTensorType:
element_type = quantized_tensor_element_type(x)
baseline_element_type = QuantizedTensorElementType(
storage_type = storage_type(element_type),
storage_min = storage_min(element_type),
storage_max = storage_max(element_type),
expressed_type = expressed_type(element_type),
quantization_dimension = quantization_dimension(element_type),
scales = [constant(1.0, expressed_type(element_type))] * dim(x, quantization_dimension(element_type)),
zero_points = [constant(0, storage_type(element_type))] * dim(x, quantization_dimension(element_type)))
return QuantizedTensorType(shape(x), baseline_element_type)
if type(x) is not Type:
return baseline_element_type(type(x))
dequantizeđược xác định trên các loại tensor được lượng tử hoá và chuyển chúng thành các loại tensor dấu phẩy động. Điều này xảy ra thông qua việc chuyển đổi các phần tử được lượng tử hoá (biểu thị các giá trị số nguyên của loại bộ nhớ) thành các giá trị dấu phẩy động tương ứng của loại được biểu thị bằng cách sử dụng điểm 0 và tỷ lệ liên kết với loại phần tử được lượng tử hoá.
def compute_zero_points(quantized_type, result_type):
if is_per_tensor_quantized(quantized_type):
return broadcast_in_dim(constant(zero_point(quantized_type), storage_type(quantized_type)), [], result_type)
if is_per_axis_quantized(quantized_type):
for i in index_space(result_type):
d = quantization_dimension(quantized_type)
zero_points[i] = zero_points(quantized_type)[i[d]]
return zero_points
def compute_scales(quantized_type, result_type):
if is_per_tensor_quantized(quantized_type):
return broadcast_in_dim(constant(scale(quantized_type), expressed_type(quantized_type)), [],
type(result_type))
if is_per_axis_quantized(quantized_type):
for i in index_space(result_type):
d = quantization_dimension(quantized_type)
scales[i] = scales(quantized_type)[i[d]]
return scales
def dequantize(x: Value) -> Value:
assert is_quantized(x)
x_storage = bitcast_convert(x, storage_type(x))
x_storage_sub = x_storage - compute_zero_points(type(x), type(x_storage))
x_expressed_sub = convert(x_storage_sub, expressed_type(x))
return x_expressed_sub * compute_scales(type(x), type(x_expressed_sub))
quantizeđược xác định trên các loại tensor dấu phẩy động và chuyển chúng thành các loại tensor được lượng tử hoá. Điều này xảy ra thông qua việc chuyển đổi các giá trị dấu phẩy động của loại được biểu thị thành các giá trị số nguyên tương ứng của loại lưu trữ bằng cách sử dụng điểm không và tỷ lệ liên kết với loại phần tử được lượng tử hoá.
def quantize(x: Value, result_type: Type) -> Value:
assert is_float(x) and is_quantized(result_type)
zero_points = compute_zero_points(result_type, TensorType(shape(x), storage_type(result_type)))
converted_zero_points = convert(zero_points, expressed_type(result_type))
converted_min = convert(storage_min(result_type), expressed_type(result_type))
converted_max = convert(storage_max(result_type), expressed_type(result_type))
x_scaled = x / compute_scales(result_type, type(x))
x_scaled_add_zp = x_scaled + converted_zero_points
x_clamped = clamp(converted_min, x_scaled_add_zp, converted_max)
x_rounded = round_nearest_even(x_clamped)
return convert(x_rounded, result_type)
dequantize_op_quantizeđược dùng để chỉ định các phép tính theo phần tử trên các tensor được lượng tử hoá. Thao tác này sẽ khử lượng tử hoá, tức là chuyển các phần tử được lượng tử hoá thành các loại được biểu thị, sau đó thực hiện một thao tác rồi lượng tử hoá, tức là chuyển kết quả trở lại các loại lưu trữ. Hiện tại, hàm này chỉ hoạt động cho quá trình định lượng trên mỗi tensor. Lượng tử hoá theo trục đang trong quá trình phát triển (#1574).
def dequantize_op_quantize(op, *inputs_and_output_type):
inputs = inputs_and_output_type[:-1]
output_type = inputs_and_output_type[-1]
float_inputs = map(dequantize, inputs)
float_result = op(*float_inputs)
return quantize(float_result, output_type)
def dequantize_batch_norm_grad_or_training_quantize(op, *inputs_and_output_types):
inputs = inputs_and_output_type[:-3]
float_inputs = map(dequantize, inputs)
float_results = op(*float_inputs)
return map(quantize, float_results, inputs_and_output_type[-3:])
def dequantize_compare(lhs, rhs, comparison_direction):
float_lhs = dequantize(lhs)
float_rhs = dequantize(rhs)
return compare(float_lhs, float_rhs, comparison_direction, FLOAT)
def dequantize_select_quantize(pred, on_true, on_false, output_type):
float_on_true = dequantize(on_true)
float_on_false = dequantize(on_false)
float_result = select(pred, float_on_true, float_on_false)
return quantize(float_result, output_type)
hybrid_dequantize_then_opđược dùng để chỉ định chỉ định lượng chỉ trọng số cho thao tác kết hợp chấp nhận lhs ở dấu phẩy động và rhs ở các loại được định lượng. Thao tác này giải lượng tử hoá các đầu vào được lượng tử hoá thành các loại được biểu thị và thực hiện tính toán ở dạng số thực. Loại phần tử của tensor lhs có dấu phẩy động và loại được biểu thị của tensor rhs được lượng tử hoá phải giống nhau.
def hybrid_dequantize_then_op(op, lhs, rhs):
assert(is_float(lhs) and is_quantized(rhs) and element_type(lhs) == expressed_type(rhs))
return op(lhs, dequantize(rhs))
Tính toán lưới
cross_partition(replica_groups: Value) -> Value. Xem phần "cross_replica" ở trên.cross_replica(replica_groups: Value) -> Value. Xem phần "cross_replica" ở trên.cross_replica_and_partition(replica_groups: Value) -> Value. Xem phần "cross_replica_and_partition" ở trên.flattened_ids(replica_groups: Value) -> Value. Xem phần "flattened_ids" ở trên.
Tính năng động
Các giá trị StableHLO có thể có kích thước phương diện linh động, ví dụ: tensor<?xi64>.
Tuy nhiên, các giá trị StableHLO không thể có số lượng phương diện động (tính linh hoạt không được xếp hạng, ví dụ: tensor<*xi64>). Các toán hạng và kết quả được phép sử dụng kích thước phương diện động, ngay cả khi có các ràng buộc về kích thước. Các ràng buộc sẽ được xác minh tĩnh nếu có thể, nếu không, chúng sẽ được hoãn lại đến thời gian chạy và các trường hợp không khớp sẽ dẫn đến hành vi không xác định. Hãy xem ví dụ bên dưới.
Hình dạng không khớp cho các phép toán đơn nguyên theo phần tử
Hãy xem xét chương trình đồ chơi sau:
func.func @foo(%arg0: tensor<?xf64>) {
%0 = stablehlo.abs %arg0 : (tensor<?xf64>) -> tensor<2xf64>
return
}
Chương trình như vậy là không bình thường, vì không phổ biến khi biết hình dạng của kết quả nhưng không biết hình dạng của đầu vào. Tuy nhiên, đây là một chương trình StableHLO hợp lệ. Không thể xác thực tĩnh hoạt động abs trong chương trình này vì không biết chính xác hình dạng của toán hạng. Tuy nhiên, các hình dạng chắc chắn tương thích và bạn có thể kiểm tra tĩnh điều này: ? có thể trở thành 2 trong thời gian chạy và sẽ không có vấn đề gì. Tuy nhiên, ? cũng có thể là một số nguyên khác, trong trường hợp đó, hành vi sẽ không xác định.
Lưu ý rằng nếu kích thước phương diện là động trong kết quả, thì không thể có hành vi không xác định. Thật vậy, không có kích thước "dự kiến" nên không thể có sự không khớp.
Hình dạng không khớp cho các thao tác nhị phân theo phần tử
Hãy xem xét chương trình đồ chơi sau:
func.func @foo(%arg0: tensor<?xf64>, %arg1: tensor<?xf64>) {
%0 = stablehlo.add %arg0, %arg0 : (tensor<?xf64>, tensor<?xf64>) -> tensor<?xf64>
return
}
Đối với các thao tác nhị phân theo phần tử, hình dạng của dữ liệu đầu vào và kết quả phải khớp với nhau trong thời gian chạy. Tại thời gian biên dịch, các phương diện tĩnh phải bằng nhau, nếu không, chúng chỉ cần tương thích. Nếu bất kỳ phương diện nào là động trong các đầu vào, thì có thể có hành vi không xác định tại thời gian chạy, vì kích thước động có thể không khớp với kích thước tương ứng trong toán hạng khác (cho dù đó là kích thước tĩnh hay động). Nếu tất cả các đầu vào đều tĩnh, thì việc kết quả có động hay không không quan trọng: các phương diện đã biết tĩnh sẽ được kiểm tra tĩnh và các phương diện động không áp đặt bất kỳ ràng buộc nào.
Hình dạng không khớp cho các thao tác lấy hình dạng đầu ra làm toán hạng
Hãy xem xét chương trình đồ chơi sau:
func.func @foo(%arg0: tensor<2xi32>) {
%0 = stablehlo.dynamic_iota %arg0, dim = 0 : (tensor<2xi32>) -> tensor<3x4xi64>
return
}
Các giá trị trong toán hạng hình dạng tại thời gian chạy phải khớp với hình dạng của kết quả, nếu không, hành vi sẽ không xác định. Tức là trong thời gian chạy, %arg0 phải có giá trị là dense<[3, 4]> : tensor<2xi32>. Nếu toán hạng hình dạng là hằng số, thì điều này có thể được xác minh một cách tĩnh. Nếu hình dạng kết quả hoàn toàn linh hoạt, thì không thể có sự không khớp.