StableHLO는 머신러닝 (ML) 모델의 고급 작업 (HLO)을 위한 작업 집합입니다. StableHLO는 다양한 ML 프레임워크와 ML 컴파일러 간의 이식성 레이어로 작동합니다. StableHLO 프로그램을 생성하는 ML 프레임워크는 StableHLO 프로그램을 사용하는 ML 컴파일러와 호환됩니다.
Google의 목표는 다양한 ML 프레임워크 (예: TensorFlow, JAX, PyTorch)와 ML 컴파일러 (예: XLA, IREE) 간의 상호 운용성을 높여 ML 개발을 간소화하고 가속화하는 것입니다. 이를 위해 이 문서에서는 StableHLO 프로그래밍 언어의 사양을 제공합니다.
이 사양에는 세 가지 주요 섹션이 포함되어 있습니다. 먼저 프로그램 섹션에서는 StableHLO 프로그램의 구조를 설명합니다. StableHLO 프로그램은 StableHLO 함수로 구성되며 StableHLO 함수는 StableHLO 작업으로 구성됩니다. 이 구조 내에서 Ops 섹션은 개별 작업의 의미를 지정합니다. 실행 섹션에서는 프로그램 내에서 함께 실행되는 이러한 모든 작업의 시맨틱스를 제공합니다. 마지막으로 표기법 섹션에서는 사양 전체에서 사용되는 표기법을 설명합니다.
이전 버전의 StableHLO 사양을 보려면 원하는 태그가 지정된 버전에서 저장소를 여세요. 예를 들어 StableHLO v0.19.0 사양을 참고하세요. StableHLO의 각 부버전 업데이트에서 발생한 변경사항을 확인하려면 VhloDialect.td의 버전 로그를 참고하세요.
프로그램
Program ::= {Func}
StableHLO 프로그램은 임의의 수의 StableHLO 함수로 구성됩니다.
다음은 입력이 3개(%image, %weights, %bias)이고 출력이 1개인 함수 @main가 있는 프로그램의 예입니다. 함수의 본문에는 6개의 작업이 있습니다.
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>) -> ()
}
함수
Func ::= 'func' '.' 'func' FuncId FuncInputs FuncOutputs '{' FuncBody '}'
FuncInputs ::= '(' [FuncInput {',' FuncInput}] `)`
FuncInput ::= ValueId ':' ValueType
FuncOutputs ::= ['->' FuncOutput, {',' FuncOutput}]
FuncOutput ::= ValueType
FuncBody ::= {Op}
StableHLO 함수 (이름이 지정된 함수라고도 함)에는 식별자, 입력/출력, 본문이 있습니다. 향후 함수에 추가 메타데이터를 도입하여 HLO와의 호환성을 개선할 계획입니다 (#425, #626, #740, #744).
식별자
FuncId ::= '@' letter {letter | digit}
ValueId ::= '%' digit {digit}
| '%' letter {letter | digit}
letter ::= 'a' | ... | 'z' | 'A' | ... | 'Z' | '_'
digit ::= '0' | ... | '9'
StableHLO 식별자는 여러 프로그래밍 언어의 식별자와 유사하며 두 가지 특이사항이 있습니다. 1) 모든 식별자에는 다양한 종류의 식별자를 구분하는 시길이 있습니다. 2) 값 식별자는 StableHLO 프로그램 생성을 단순화하기 위해 완전히 숫자일 수 있습니다.
유형
Type ::= ValueType | NonValueType
ValueType ::= TensorType | QuantizedTensorType | TokenType | TupleType | BufferType
NonValueType ::= TensorElementType | QuantizedTensorElementType | FunctionType | StringType
StableHLO 유형은 StableHLO 값을 나타내는 값 유형 (일급 유형이라고도 함)과 기타 프로그램 요소를 설명하는 비값 유형으로 분류됩니다. StableHLO 유형은 여러 프로그래밍 언어의 유형과 유사하며, 주요 특이점은 StableHLO의 도메인별 특성으로 인해 일부 비정상적인 결과가 발생한다는 것입니다 (예: 스칼라 유형은 값 유형이 아님).
TensorType ::= 'tensor' '<' Shape TensorElementType '>'
Shape ::= {DimensionSize 'x'}
DimensionSize ::= digit {digit} | '?'
텐서 유형은 텐서, 즉 다차원 배열을 나타냅니다. 모양과 요소 유형이 있으며, 여기서 모양은 0부터 R-1까지 번호가 매겨진 해당 차원 (축이라고도 함)의 오름차순으로 음수가 아닌 차원 크기 또는 알 수 없는 차원 크기를 나타냅니다. 차원 수 R을 순위라고 합니다. 예를 들어 tensor<2x3xf32>은 모양이 2x3이고 요소 유형이 f32인 텐서 유형입니다. 크기가 2와 3인 두 개의 차원(즉, 두 개의 축)인 0번째 차원과 1번째 차원이 있습니다. 순위는 2입니다.
모양은 부분적으로 또는 완전히 알 수 없을 수 있습니다 (동적). 예를 들어 tensor<?x2xf64>는 부분적으로 알 수 없고 tensor<?x?xf64>는 완전히 알 수 없습니다. 동적 측정기준 크기는 ?를 사용하여 표현됩니다. 모양은 순위를 해제할 수 없습니다.
향후에는 텐서 유형을 차원 크기 및 요소 유형을 넘어 레이아웃(#629) 및 희소성(#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
| 이름 | 유형 | 제약조건 |
|---|---|---|
storage_type |
정수 유형 | (C1~C3), (C8) |
storage_min |
정수 상수 | (C1), (C3), (C7) |
storage_max |
정수 상수 | (C2), (C3), (C7) |
expressed_type |
부동 소수점 유형 | (C4) |
quantization_dimension |
선택적 정수 상수 | (C10~C12) |
scales |
가변 개수의 부동 소수점 상수 | (C4~C6), (C9), (C10), (C13) |
zero_points |
가변 개수의 정수 상수 | (C7~C9) |
양자화된 요소 유형은 표현된 유형의 부동 소수점 값에 해당하는 storage_min~storage_max (포함) 범위의 저장 유형 정수 값을 나타냅니다. 주어진 정수 값 i의 경우 해당 부동 소수점 값 f은 f = (i - zero_point) * scale로 계산할 수 있습니다. 여기서 scale 및 zero_point을 양자화 매개변수라고 합니다. storage_min 및 storage_max는 문법에서 선택사항이지만 기본값은 각각 min_value(storage_type) 및 max_value(storage_type)입니다. 양자화된 요소 유형에는 다음과 같은 제약 조건이 있습니다.
- (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)
is_empty(quantization_dimension)인 경우size(scales) = 1입니다. - (C11)
0 <= quantization_dimension.
현재 QuantizationScale는 부동 소수점 상수이지만, 승수와 시프트로 표현되는 정수 기반 스케일에 대한 관심이 높습니다. Google에서는 가까운 시일 내에 이 문제를 살펴볼 계획입니다(#1404).
양자화된 텐서 유형의 유형, 값, 0점이 하나만 있을 수 있는지 아니면 여러 개 있을 수 있는지 등 QuantizationZeroPoint의 의미에 관한 논의가 진행 중입니다. 이 토론의 결과에 따라 0점 관련 사양이 향후 변경될 수 있습니다 (#1405).
또 다른 진행 중인 논의는 QuantizationStorageMin 및 QuantizationStorageMax의 시맨틱을 포함하여 이러한 값과 양자화된 텐서의 값에 제약 조건을 적용해야 하는지 결정하는 것입니다(#1406).
마지막으로 알 수 없는 스케일과 0점을 표현하는 방법을 알 수 없는 차원 크기를 표현하는 방법과 유사하게 살펴볼 계획입니다 (#1407).
양자화된 텐서 유형은 양자화된 요소가 있는 텐서를 나타냅니다. 이러한 텐서는 일반 텐서와 정확히 동일하지만 요소가 일반 요소 유형 대신 양자화된 요소 유형을 갖습니다.
양자화된 텐서에서 양자화는 텐서별일 수 있습니다. 즉, 전체 텐서에 하나의 scale 및 zero_point가 있거나 축별일 수 있습니다. 즉, 특정 차원 quantization_dimension의 슬라이스당 한 쌍의 여러 scales 및 zero_points가 있습니다. 더 공식적으로 축별 양자화가 적용된 텐서 t에는 quantization_dimension의 슬라이스가 dim(t, quantization_dimension)개 있습니다(t[:, ..., 0, ..., :], t[:, ..., 1, ..., :] 등). i번째 슬라이스의 모든 요소는 scales[i] 및 zero_points[i]를 양자화 매개변수로 사용합니다. 양자화된 텐서 유형에는 다음과 같은 제약이 있습니다.
- 텐서별 양자화의 경우 다음을 충족해야 합니다.
- 추가 제약 조건이 없습니다.
- 축별 양자화의 경우 다음을 충족해야 합니다.
- (C12)
quantization_dimension < rank(self). - (C13)
dim(self, quantization_dimension) = size(scales).
- (C12)
TokenType ::= 'token'
토큰 유형은 토큰, 즉 일부 작업에서 생성하고 사용하는 불투명한 값을 나타냅니다. 토큰은 실행 섹션에 설명된 대로 작업에 실행 순서를 적용하는 데 사용됩니다.
TupleType ::= 'tuple' '<' TupleElementTypes '>'
TupleElementTypes ::= [ValueType {',' ValueType}]
버퍼 유형은 버퍼를 나타냅니다. 예를 들어 XLA에서 버퍼는 일관된 스토리지가 있는 다차원 배열입니다. 텐서 유형과 마찬가지로 버퍼 유형에는 모양과 요소 유형이 있습니다. 여기서 모양은 0부터 R-1까지 번호가 매겨진 해당 차원 (축이라고도 함)의 오름차순으로 음수가 아닌 차원 크기 또는 알 수 없는 차원 크기를 나타냅니다. 측정기준 수 R를 순위라고 합니다. 예를 들어 memref<2x3xf32>은 모양이 2x3이고 요소 유형이 f32인 버퍼 유형입니다. 크기가 2와 3인 두 개의 차원 (즉, 두 개의 축)인 0번째 차원과 1번째 차원이 있습니다. 순위는 2입니다.
버퍼는 custom_call~CreateBuffer 또는 Pin를 사용하여 할당하고 custom_call~Unpin를 통해 할당 해제할 수 있습니다. custom_call 작업만 버퍼 내부의 콘텐츠를 읽고 쓸 수 있습니다. 자세한 내용은 custom_call을 참고하세요.
튜플 유형은 튜플, 즉 이질적인 목록을 나타냅니다. 튜플은 HLO와의 호환성을 위해서만 존재하는 기존 기능입니다. HLO에서 튜플은 가변 입력과 출력을 나타내는 데 사용됩니다. StableHLO에서는 가변 입력과 출력이 기본적으로 지원되며 StableHLO에서 튜플을 사용하는 유일한 경우는 특정 구현에 따라 T, tuple<T>, tuple<tuple<T>>이 실질적으로 다를 수 있는 HLO ABI를 포괄적으로 표현하는 경우입니다. 향후 HLO ABI를 변경하여 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'
요소 유형은 텐서 유형의 요소를 나타냅니다. 많은 프로그래밍 언어와 달리 이러한 유형은 StableHLO에서 최상위 클래스가 아닙니다. 즉, StableHLO 프로그램은 이러한 유형의 값을 직접 나타낼 수 없습니다. 따라서 T 유형의 스칼라 값을 tensor<T> 유형의 0차원 텐서 값으로 나타내는 것이 관용적입니다.
- 불리언 유형은 불리언 값
true및false를 나타냅니다. - 정수 유형은 부호가 있거나 (
si) 부호가 없을 수 있으며 (ui) 지원되는 비트 너비 (2,4,8,16,32또는64) 중 하나를 가집니다. 부호 있는siN유형은-2^(N-1)부터2^(N-1)-1까지의 정수 값을 나타내고 부호 없는uiN유형은0부터2^N-1까지의 정수 값을 나타냅니다. - 부동 소수점 유형은 다음 중 하나일 수 있습니다.
- IEEE-754 규칙을 따르는
f8E3M4,f8E4M3,f8E5M28비트 부동 소수점 숫자입니다. - 딥 러닝을 위한 FP8 형식에 설명된 FP8 형식의
E4M3및E5M2인코딩에 각각 해당하는f8E4M3FN및f8E5M2유형 - 심층 신경망을 위한 8비트 숫자 형식에 설명된 FP8 형식의
E4M3및E5M2인코딩에 해당하는f8E4M3FNUZ및f8E5M2FNUZ유형 - 심층 신경망을 위한 하이브리드 8비트 부동 소수점 (HFP8) 학습 및 추론에 설명된 FP8 형식의
E4M3인코딩에 해당하는f8E4M3B11FNUZ유형 - BFloat16: Cloud TPU의 고성능을 위한 비법에 설명된
bfloat16형식에 해당하는bf16유형입니다. - 각각 IEEE 754 표준에 설명된
binary16('절반 정밀도'),binary32('단일 정밀도'),binary64('배정밀도') 형식에 해당하는f16,f32,f64유형입니다. tf32유형은 TensorFloat32 형식에 해당하며 StableHLO에서 지원이 제한됩니다.- OCP 마이크로스케일링 형식 사양에 설명된
f4E2M1FN,f6E2M3FN,f6E3M2FN,f8E8M0FNUMX (마이크로스케일링) 유형
- IEEE-754 규칙을 따르는
- 복합 유형은 동일한 요소 유형의 실수부와 허수부가 있는 복합 값을 나타냅니다. 지원되는 복잡한 유형은
complex<f32>(두 부분이 모두f32유형임) 및complex<f64>(두 부분이 모두f64유형임)입니다.
FunctionType ::= '(' InputTypes ')' '->' '(' OutputTypes ')'
InputTypes ::= [ValueType {',' ValueType}]
OutputTypes ::= [ValueType {',' ValueType}]
함수 유형은 이름이 지정된 함수와 익명 함수를 모두 나타냅니다. 입력 유형 (->의 왼쪽에 있는 유형 목록)과 출력 유형(->의 오른쪽에 있는 유형 목록)이 있습니다. 많은 프로그래밍 언어에서 함수 유형은 일급이지만 StableHLO에서는 그렇지 않습니다.
StringType ::= 'string'
문자열 유형은 바이트 시퀀스를 나타냅니다. 많은 프로그래밍 언어와 달리 문자열 유형은 StableHLO에서 일급이 아니며 프로그램 요소의 정적 메타데이터를 지정하는 데만 사용됩니다.
작업
StableHLO 작업 (작업이라고도 함)은 머신러닝 모델의 폐쇄된 고급 작업 집합을 나타냅니다. 위에서 설명한 것처럼 StableHLO 구문은 MLIR에서 많은 영감을 받았습니다. MLIR이 반드시 가장 인체공학적인 대안은 아니지만 ML 프레임워크와 ML 컴파일러 간의 상호 운용성을 높이려는 StableHLO의 목표에 가장 적합하다고 할 수 있습니다.
Op ::= [OpOutputs] OpName OpInputs ':' OpSignature
OpName ::= '"' 'stablehlo' '.' OpMnemonic '"'
OpMnemonic ::= 'abs' | 'add' | ...
StableHLO 작업 (작업이라고도 함)에는 이름, 입력/출력, 서명이 있습니다. 이름은 stablehlo. 접두사와 지원되는 작업을 고유하게 식별하는 니모닉으로 구성됩니다. 지원되는 모든 작업의 전체 목록은 아래를 참고하세요.
OpInputs ::= OpInputValues OpInputFuncs OpInputAttrs
OpInputValues ::= '(' [OpInputValue {',' OpInputValue}] ')'
OpInputValue ::= ValueId
OpInputFuncs ::= ['(' OpInputFunc {',' OpInputFunc} ')']
OpInputAttrs ::= ['{' OpInputAttr {',' OpInputAttr} '}']
OpOutputs ::= [OpOutput {',' OpOutput} '=']
OpOutput ::= ValueId
작업은 입력을 사용하고 출력을 생성합니다. 입력은 입력 값 (실행 중에 계산됨), 입력 함수 (StableHLO에서는 함수가 최고 수준 값이 아니므로 정적으로 제공됨), 입력 속성 (정적으로 제공됨)으로 분류됩니다. 작업에서 소비하고 생성하는 입력 및 출력 종류는 니모닉에 따라 다릅니다. 예를 들어 add 작업은 입력 값 2개를 사용하고 출력 값 1개를 생성합니다. 이에 비해 select_and_scatter 작업은 입력 값 3개, 입력 함수 2개, 입력 속성 3개를 사용합니다.
OpInputFunc ::= '{' Unused FuncInputs ':' FuncBody '}'
Unused ::= '^' digit {digit}
| '^' letter {letter | digit}
입력 함수 (익명 함수라고도 함)는 다음을 제외하고 이름이 지정된 함수와 매우 유사합니다. 1) 식별자가 없습니다('익명'이라는 이름이 붙은 이유). 2) 출력 유형을 선언하지 않습니다 (출력 유형은 함수 내의 return 작업에서 추론됨).
입력 함수의 구문에는 현재 사용되지 않는 부분이 포함되어 있습니다 (위의 Unused 프로덕션 참고). 이는 MLIR과의 호환성을 위한 것입니다. MLIR에는 점프 작업으로 연결된 여러 작업 '블록'이 있을 수 있는 '리전'이라는 더 일반적인 개념이 있습니다. 이러한 블록에는 Unused 프로덕션에 해당하는 ID가 있어 서로 구분할 수 있습니다.
StableHLO에는 점프 작업이 없으므로 MLIR 구문의 해당 부분은 사용되지 않습니다 (하지만 여전히 존재함).
OpInputAttr ::= OpInputAttrName '=' OpInputAttrValue
OpInputAttrName ::= letter {letter | digit}
OpInputAttrValue ::= Constant
입력 속성에는 이름과 지원되는 상수 중 하나의 값이 있습니다. 이는 프로그램 요소의 정적 메타데이터를 지정하는 기본 방법입니다. 예를 들어 concatenate 작업은 dimension 속성을 사용하여 입력 값이 연결되는 차원을 지정합니다. 마찬가지로 slice 작업은 start_indices, limit_indices과 같은 여러 속성을 사용하여 입력 값을 슬라이스하는 데 사용되는 경계를 지정합니다.
현재 실제 StableHLO 프로그램에는 이 문서에 설명되지 않은 속성이 포함되는 경우가 있습니다. 향후 이러한 속성을 StableHLO opset에 흡수하거나 StableHLO 프로그램에 표시되지 않도록 금지할 계획입니다. 그동안 다음 속성 목록을 확인하세요.
layout(#629)mhlo.frontend_attributes(#628).mhlo.sharding(#619).output_operand_aliases(#740)- 위치 메타데이터 (#594)
OpSignature ::= '(' [ValueType {',' ValueType}] ')' '->' '(' [ValueType {',' ValueType}] ')'
작업 서명은 모든 입력 값의 유형 (->의 왼쪽 유형 목록)과 모든 출력 값의 유형 (->의 오른쪽 유형 목록)으로 구성됩니다. 엄밀히 말하면 입력 유형은 중복되고 출력 유형도 거의 항상 중복됩니다 (대부분의 StableHLO 작업의 경우 출력 유형은 입력에서 추론할 수 있기 때문). 하지만 MLIR과의 호환성을 위해 op 서명은 의도적으로 StableHLO 구문의 일부입니다.
아래는 니모닉이 select_and_scatter인 작업의 예입니다. 3개의 입력 값 (%operand, %source, %init_value), 2개의 입력 함수, 3개의 입력 속성 (window_dimensions, window_strides, padding)을 사용합니다. 작업의 서명에는 입력 값의 유형만 포함됩니다(인라인으로 제공되는 입력 함수 및 속성의 유형은 포함되지 않음).
%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>
상수
Constant ::= BooleanConstant
| IntegerConstant
| FloatConstant
| ComplexConstant
| TensorConstant
| QuantizedTensorConstant
| StringConstant
| EnumConstant
StableHLO 상수에는 리터럴과 유형이 있으며, 이 둘이 함께 StableHLO 값을 나타냅니다. 일반적으로 유형은 상수 구문의 일부입니다. 단, 명확한 경우 (예: 불리언 상수는 명확하게 i1 유형을 갖는 반면 정수 상수는 여러 가능한 유형을 가질 수 있음)는 예외입니다.
BooleanConstant ::= BooleanLiteral
BooleanLiteral ::= 'true' | 'false'
불리언 상수는 불리언 값 true 및 false을 나타냅니다. 불리언 상수의 유형은 i1입니다.
IntegerConstant ::= IntegerLiteral ':' IntegerType
IntegerLiteral ::= ['-' | '+'] DecimalDigits
| ['-' | '+'] '0x' HexadecimalDigits
DecimalDigits ::= decimalDigit {decimalDigit}
HexadecimalDigits ::= hexadecimalDigit {hexadecimalDigit}
decimalDigit ::= '0' | ... | '9'
hexadecimalDigit ::= decimalDigit | 'a' | ... | 'f' | 'A' | ... | 'F'
정수 상수는 10진수 또는 16진수 표기법을 사용하는 문자열을 통해 정수 값을 나타냅니다. 이진수 또는 8진수와 같은 다른 진수는 지원되지 않습니다. 정수 상수에는 다음과 같은 제약이 있습니다.
- (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]
부동 소수점 상수는 십진수 또는 과학적 표기법을 사용하는 문자열을 통해 부동 소수점 값을 나타냅니다. 또한 16진수 표기법을 사용하여 해당 유형의 부동 소수점 형식에서 기본 비트를 직접 지정할 수 있습니다. 부동 소수점 상수에는 다음과 같은 제약이 있습니다.
- (C1) 16진수 표기법이 아닌 표기법을 사용하는 경우
is_wellformed(float_literal, float_type) - (C2) 16진수 표기법을 사용하는 경우
size(hexadecimal_digits) = num_bits(float_type) / 4
ComplexConstant ::= ComplexLiteral ':' ComplexType
ComplexLiteral ::= '(' RealPart ',' ImaginaryPart ')'
RealPart ::= FloatLiteral
ImaginaryPart ::= FloatLiteral
복소수 상수는 실수부(먼저 나옴)와 허수부 (두 번째로 나옴)의 목록을 사용하여 복소수 값을 나타냅니다. 예를 들어 (1.0, 0.0) : complex<f32>은 1.0 + 0.0i를 나타내고 (0.0, 1.0) : complex<f32>는 0.0 + 1.0i를 나타냅니다. 이러한 부분이 메모리에 저장되는 순서는 구현에 따라 다릅니다. 복잡한 상수에는 다음과 같은 제약 조건이 있습니다.
- (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
텐서 상수는 NumPy 표기법을 통해 지정된 중첩 목록을 사용하여 텐서 값을 나타냅니다. 예를 들어 dense<[[1, 2, 3], [4, 5, 6]]> : tensor<2x3xi32>는 색인에서 요소로의 다음 매핑이 있는 텐서 값을 나타냅니다. {0, 0} => 1, {0, 1} => 2, {0, 2} => 3, {1, 0} => 4, {1, 1} => 5, {1, 2} => 6 이러한 요소가 메모리에 저장되는 순서는 구현에 따라 다릅니다. 텐서 상수에는 다음과 같은 제약이 있습니다.
- (C1)
has_syntax(tensor_literal, element_type(tensor_type)), 여기서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)), 여기서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:]).- 그 외의 경우
false입니다.
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'
양자화 텐서 상수는 텐서 상수와 동일한 표기법을 사용하여 양자화 텐서 값을 나타내며, 요소는 저장 유형의 상수로 지정됩니다. 양자화된 텐서 상수에는 다음과 같은 제약이 있습니다.
- (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))
문자열 리터럴은 ASCII 문자와 이스케이프 시퀀스를 사용하여 지정된 바이트로 구성됩니다. 인코딩에 구애받지 않으므로 이러한 바이트의 해석은 구현에 따라 정의됩니다. 문자열 리터럴의 유형은 string입니다.
작업
abs
시맨틱스
operand 텐서에 요소별 abs 연산을 수행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부호 있는 정수의 경우: 정수 모듈러스입니다.
- 부동 소수점의 경우: IEEE-754의
abs - 복소수의 경우 복소수 모듈러스입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(abs, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부호 있는 정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1-C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부호 있는 정수 또는 부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1-C2) |
제약조건
- (C1)
shape(result) = shape(operand). - (C2)
baseline_element_type(result)은 다음과 같이 정의됩니다.is_complex(operand)인 경우complex_element_type(element_type(operand))- 그 밖의 경우에는
baseline_element_type(operand)입니다.
예
// %operand: [-2, 0, 2]
%result = "stablehlo.abs"(%operand)< : (t>ens>or3xi32<) - t>ensor3xi32
// %result: [2, 0, 2]
추가
시맨틱스
두 텐서 lhs와 rhs의 요소별 덧셈을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 불리언: 논리합
- 정수의 경우 정수 덧셈입니다.
- 부동 소수점의 경우: IEEE-754의
addition - 복소수의 경우 복소수 덧셈입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(add, lhs, rhs, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
텐서 또는 양자화된 텐서 | (C1-C6) |
| (I2) | rhs |
텐서 또는 양자화된 텐서 | (C1~C5), (C7) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 양자화된 텐서 | (C1-C7) |
제약조건
- 작업에서 양자화되지 않은 텐서를 사용하는 경우 다음을 충족해야 합니다.
- (C1)
type(lhs) = type(rhs) = type(result).
- (C1)
- 작업에서 양자화된 텐서를 사용하는 경우 다음을 충족해야 합니다.
- (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)
is_per_axis_quantized(lhs)시quantization_dimension(lhs) = quantization_dimension(result) - (C7)
is_per_axis_quantized(rhs)시quantization_dimension(rhs) = quantization_dimension(result).
- (C2)
예
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.add"(%lhs, %rhs)< : (ten>sor2x2xi<32, ten>sor>2x2xi32<) - ten>sor2x2xi32
// %result: [[6, 8], [10, 12]]
after_all
시맨틱스
inputs를 생성하는 작업이 result에 종속된 작업보다 먼저 실행되도록 합니다. 이 작업의 실행은 아무것도 하지 않으며 result에서 inputs로의 데이터 종속 항목을 설정하기 위해서만 존재합니다.
입력
| 라벨 | 이름 | 유형 |
|---|---|---|
| (I1) | inputs |
가변적인 token 수 |
출력
| 이름 | 유형 |
|---|---|
result |
token |
예
// %input0: !stablehlo.token
// %input1: !stablehlo.token
%result = "stablehlo.after_all"(%input0, %input1) : (!stablehlo.token, !stablehl>o.token) - !stablehlo.token
all_gather
시맨틱스
StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 각 프로세스의 operands 텐서 값을 all_gather_dim에 따라 연결하고 results 텐서를 생성합니다.
이 작업은 StableHLO 프로세스 그리드를 다음과 같이 정의된 process_groups로 분할합니다.
cross_replica(replica_groups)channel_id <= 0 and use_global_device_ids = false인 경우cross_replica_and_partition(replica_groups)channel_id > 0 and use_global_device_ids = false인 경우flattened_ids(replica_groups)channel_id > 0 and use_global_device_ids = true인 경우
그런 다음 각 process_group 내에서 다음을 실행합니다.
process_group의 모든receiver에 대해operands...@receiver = [operand@sender for sender in process_group]process_group의 모든process에 대해results...@process = concatenate(operands...@process, all_gather_dim)
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operands |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C1), (C6) |
| (I2) | all_gather_dim |
si64 유형의 상수 |
(C1), (C6) |
| (I3) | replica_groups |
si64 유형의 2차원 텐서 상수 |
(C2-C4) |
| (I4) | channel_id |
si64 유형의 상수 |
(C5) |
| (I5) | use_global_device_ids |
i1 유형의 상수 |
(C5) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C6) |
제약조건
- (C1)
0 <= all_gather_dim < rank(operands...). - (C2)
is_unique(replica_groups) - (C3)
size(replica_groups)는 다음과 같이 정의됩니다.cross_replica이 사용된 경우num_replicascross_replica_and_partition이 사용된 경우num_replicasflattened_ids이 사용된 경우num_processes
- (C4)
0 <= replica_groups < size(replica_groups) - (C5)
use_global_device_ids = true시channel_id > 0. - (C6)
type(results...) = type(operands...)(다음 제외)dim(results..., all_gather_dim) = dim(operands..., all_gather_dim) * dim(process_groups, 1).
예
// 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_grou<ps = den>se[[0, 1]<] : ten>sor1x2xi64,
// channel_id = 0
channel_handle = #stablehlo.chan<nel_handlehandle = 0>, type = 0
// use_global_device_ids = false
}< : (ten>sor2x2xi<64, ten>sor>2x2xi64)< - (ten>sor2x4xi<64, ten>sor2x4xi64)
// %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
시맨틱스
StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 각 프로세스의 operands 텐서 값에 감소 함수 computation를 적용하고 results 텐서를 생성합니다.
이 작업은 StableHLO 프로세스 그리드를 다음과 같이 정의된 process_groups로 분할합니다.
cross_replica(replica_groups)channel_id <= 0 and use_global_device_ids = false인 경우cross_replica_and_partition(replica_groups)channel_id > 0 and use_global_device_ids = false인 경우flattened_ids(replica_groups)channel_id > 0 and use_global_device_ids = true인 경우
그런 다음 각 process_group 내에서 다음을 실행합니다.
results...@process[result_index] = exec(schedule)(일부 이진 트리schedule의 경우) 여기서exec(node)=computation(exec(node.left), exec(node.right)).exec(leaf)=leaf.value.
schedule는 중위 순회 순서가to_destination_type(operands...@process_group...[result_index], type(func_inputs(computation)[0]))인 구현 정의 바이너리 트리입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operands |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C5), (C6) |
| (I2) | replica_groups |
si64 유형의 1차원 텐서 상수의 가변 개수 |
(C1-C3) |
| (I3) | channel_id |
si64 유형의 상수 |
(C4) |
| (I4) | use_global_device_ids |
i1 유형의 상수 |
(C4) |
| (I5) | computation |
함수 | (C5) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C6~C7) |
제약조건
- (C1)
is_unique(replica_groups). - (C2)
size(replica_groups)는 다음과 같이 정의됩니다.cross_replica이 사용된 경우num_replicascross_replica_and_partition이 사용된 경우num_replicasflattened_ids이 사용된 경우num_processes
- (C3)
0 <= replica_groups < size(replica_groups). - (C4)
use_global_device_ids = true시channel_id > 0. - (C5)
computation의 유형은(tensor<E>, tensor<E>) -> (tensor<E>)이며 여기서is_promotable(element_type(operand), E)입니다. - (C6)
shape(results...) = shape(operands...) - (C7)
element_type(results...) = E.
예
// 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(%ar<g0:> tensori64, %ar<g1:> tensori64):
%0 = "stablehlo.add"(%ar<g0,> %arg1) <: (>ten>sori64,< te>nsori64) - tensori64
"stable<hlo>.re>turn"(%0) : (tensori64) - ()<
}) {
>replica_g<roups => dense[[0, 1]] : tensor1x2xi64,
// channel_id = 0
channel_hand<le = #stablehlo.chan>nel_handlehandle = 0, type = 0
// use_global_<devic>e_ids = <false>
} >: (tenso<r4xi6>4, tenso<r4xi6>4) - (tensor4xi64, tensor4xi64)
// %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
시맨틱스
StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 operands 텐서의 값을 split_dimension을 따라 부분으로 분할하고, 분할된 부분을 프로세스 간에 분산하고, 분산된 부분을 concat_dimension을 따라 연결하여 results 텐서를 생성합니다.
이 작업은 StableHLO 프로세스 그리드를 다음과 같이 정의된 process_groups로 분할합니다.
channel_id <= 0인 경우cross_replica(replica_groups)channel_id > 0인 경우cross_partition(replica_groups)
그런 다음 각 process_group 내에서 다음을 실행합니다.
process_group의 모든sender에 대한split_parts...@sender = split(operands...@sender, split_count, split_dimension)receiver_index = process_group.index(receiver)인 경우scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group]results...@process = concatenate(scattered_parts...@process, concat_dimension).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operands |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C1-C3), (C9) |
| (I2) | split_dimension |
si64 유형의 상수 |
(C1), (C2), (C9) |
| (I3) | concat_dimension |
si64 유형의 상수 |
(C3), (C9) |
| (I4) | split_count |
si64 유형의 상수 |
(C2), (C4), (C8), (C9) |
| (I5) | replica_groups |
si64 유형의 2차원 텐서 상수 |
(C5~C8) |
| (I6) | channel_id |
si64 유형의 상수 |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C9) |
제약조건
- (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)는 다음과 같이 정의됩니다.cross_replica이 사용된 경우num_replicascross_partition이 사용된 경우num_partitions
- (C7)
0 <= replica_groups < size(replica_groups). - (C8)
dim(replica_groups, 1) = split_count - (C9)
split_dimension != concat_dimension인 경우를 제외하고type(results...) = type(operands...):dim(results..., split_dimension) = dim(operands..., split_dimension) / split_count.dim(results..., concat_dimension) = dim(operands..., concat_dimension) * split_count.
예
// 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_grou<ps = den>se[[0, 1]<] : ten>sor1x2xi64
// channel_id = 0
}< : (ten>sor2x4xi<64, ten>sor>2x4xi64)< - (ten>sor4x2xi<64, ten>sor4x2xi64)
// %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]]
및
시맨틱스
두 텐서 lhs와 rhs의 요소별 AND를 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 불리언: 논리곱
- 정수의 경우 비트별 AND입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
불리언 또는 정수 유형의 텐서 | (C1) |
| (I2) | rhs |
불리언 또는 정수 유형의 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
불리언 또는 정수 유형의 텐서 | (C1) |
제약조건
- (C1)
type(lhs) = type(rhs) = type(result).
예
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.and"(%lhs, %rhs)< : (ten>sor2x2xi<32, ten>sor>2x2xi32<) - ten>sor2x2xi32
// %result: [[1, 2], [3, 0]]
atan2
시맨틱스
lhs 및 rhs 텐서에 요소별 atan2 연산을 수행하여 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
atan2 - 복소수의 경우 복소수 atan2입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(atan2, lhs, rhs, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I2) | rhs |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).
예
// %lhs: [0.0, 1.0, -1.0]
// %rhs: [0.0, 0.0, 0.0]
%result = "stablehlo.atan2"(%lhs, %rhs)< : (t>ensor3xf<64, t>ens>or3xf64<) - t>ensor3xf64
// %result: [0.0, 1.57079637, -1.57079637] // [0.0, pi/2, -pi/2]
batch_norm_grad
시맨틱스
grad_output에서 역전파되는 batch_norm_training의 여러 입력의 그라데이션을 계산하고 grad_operand, grad_scale, grad_offset 텐서를 생성합니다. 더 공식적으로 이 작업은 다음과 같이 Python 구문을 사용하여 기존 StableHLO 작업으로 분해하는 것으로 표현할 수 있습니다.
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
양자화된 유형의 경우 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))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1~C3), (C5) |
| (I2) | scale |
부동 소수점 또는 텐서별 양자화 유형의 1차원 텐서 | (C2), (C4), (C5) |
| (I3) | mean |
부동 소수점 또는 텐서별 양자화 유형의 1차원 텐서 | (C2), (C4) |
| (I4) | variance |
부동 소수점 또는 텐서별 양자화 유형의 1차원 텐서 | (C2), (C4) |
| (I5) | grad_output |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C2), (C3) |
| (I6) | epsilon |
f32 유형의 상수 |
|
| (I7) | feature_index |
si64 유형의 상수 |
(C1), (C5) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
grad_operand |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C2), (C3) |
grad_scale |
부동 소수점 또는 텐서별 양자화 유형의 1차원 텐서 | (C2), (C4) |
grad_offset |
부동 소수점 또는 텐서별 양자화 유형의 1차원 텐서 | (C2), (C4) |
제약조건
- (C1)
0 <= feature_index < rank(operand). - (C2)
operand,scale,mean,variance,grad_output,grad_operand,grad_scale,grad_offset의baseline_element_type이 동일합니다. - (C3)
operand,grad_output,grad_operand의 모양이 동일합니다. - (C4)
scale,mean,variance,grad_scale,grad_offset의 모양이 동일합니다. - (C5)
size(scale) = dim(operand, feature_index).
예
// %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
}< : (tenso>r2x2x2xf<64, t>ensor2xf<64, t>ensor2xf<64, t>ensor2xf64,
< tenso>r2x>2x2xf64)< - (tenso>r2x2x2xf<64, t>ensor2xf<64, t>ensor2xf64)
// %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
시맨틱스
feature_index 차원을 제외한 모든 차원에서 operand 텐서를 정규화하고 result 텐서를 생성합니다. 더 공식적으로 이 작업은 다음과 같이 Python 구문을 사용하여 기존 StableHLO 작업으로 분해하는 것으로 표현할 수 있습니다.
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)
양자화된 유형의 경우 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))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1-C7) |
| (I2) | scale |
부동 소수점 또는 텐서별 양자화 유형의 1차원 텐서 | (C2), (C3) |
| (I3) | offset |
부동 소수점 또는 텐서별 양자화 유형의 1차원 텐서 | (C2), (C4) |
| (I4) | mean |
부동 소수점 또는 텐서별 양자화 유형의 1차원 텐서 | (C5) |
| (I5) | variance |
부동 소수점 또는 텐서별 양자화 유형의 1차원 텐서 | (C2), (C6) |
| (I6) | epsilon |
f32 유형의 상수 |
|
| (I7) | feature_index |
si64 유형의 상수 |
(C1), (C3~C6) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C2), (C7) |
제약조건
- (C1)
0 <= feature_index < rank(operand). - (C2)
operand,scale,offset,mean,variance,result의baseline_element_type이 동일합니다. - (C3)
size(scale) = dim(operand, feature_index). - (C4)
size(offset) = dim(operand, feature_index) - (C5)
size(mean) = dim(operand, feature_index). - (C6)
size(variance) = dim(operand, feature_index) - (C7)
baseline_type(operand) = baseline_type(result).
예
// %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
}< : (tenso>r2x2x2xf<64, t>ensor2xf<64, t>ensor2xf<64, t>ensor2xf<64, t>ens>or2xf64<) - tenso>r2x2x2xf64
// %result: [
// [[0.0, 0.0], [2.0, 2.0]],
// [[2.0, 2.0], [0.0, 0.0]]
// ]
batch_norm_training
시맨틱스
feature_index 차원을 제외한 모든 차원에서 평균과 분산을 계산하고 operand 텐서를 정규화하여 output, batch_mean, batch_var 텐서를 생성합니다. 더 공식적으로 이 작업은 다음과 같이 Python 구문을 사용하여 기존 StableHLO 작업으로 분해할 수 있습니다.
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
양자화된 유형의 경우 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))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I2) | scale |
부동 소수점 또는 텐서별 양자화된 1차원 텐서 | (C2), (C3) |
| (I3) | offset |
부동 소수점 또는 텐서별 양자화된 1차원 텐서 | (C2), (C4) |
| (I4) | epsilon |
f32 유형의 상수 |
(C1), (C3~C6) |
| (I5) | feature_index |
si64 유형의 상수 |
(C1), (C3~C6) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
output |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C7) |
batch_mean |
부동 소수점 또는 텐서별 양자화된 1차원 텐서 | (C2), (C5) |
batch_var |
부동 소수점 또는 텐서별 양자화된 1차원 텐서 | (C2), (C6) |
제약조건
- (C1)
0 <= feature_index < rank(operand). - (C2)
operand,scale,offset,batch_mean,batch_var,output의baseline_element_type이 동일합니다. - (C3)
size(scale) = dim(operand, feature_index). - (C4)
size(offset) = dim(operand, feature_index) - (C5)
size(batch_mean) = dim(operand, feature_index). - (C6)
size(batch_var) = dim(operand, feature_index) - (C7)
baseline_type(output) = baseline_type(operand).
예
// %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
}< : (tenso>r2x2x2xf<64, t>ensor2xf<64, t>ens>or2xf64) -
< (tenso>r2x2x2xf<64, t>ensor2xf<64, t>ensor2xf64)
// %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
시맨틱스
operand 텐서에서 비트캐스트 작업을 실행하고 전체 operand 텐서의 비트가 result 텐서의 유형을 사용하여 재해석되는 result 텐서를 생성합니다.
더 공식적으로 E = element_type(operand), E' = element_type(result), R = rank(operand)가 주어졌을 때:
num_bits(E') < num_bits(E)인 경우bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1])입니다.num_bits(E') > num_bits(E)인 경우bits(result[i0, ..., iR-2]) = bits(operand[i0, ..., iR-2, :])입니다.num_bits(E') = num_bits(E)인 경우bits(result[i0, ..., iR-1]) = bits(operand[i0, ..., iR-1])입니다.
bits는 지정된 값의 메모리 내 표현을 반환하며 텐서의 정확한 표현이 구현에 따라 정의되고 요소 유형의 정확한 표현도 구현에 따라 정의되므로 동작이 구현에 따라 정의됩니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 양자화된 텐서 | (C1-C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 양자화된 텐서 | (C1-C2) |
제약조건
- (C1)
E = is_quantized(operand) ? storage_type(operand) : element_type(operand),E' = is_quantized(result) ? storage_type(result) : element_type(result),R = rank(operand)가 주어졌을 때:num_bits(E') = num_bits(E)인 경우shape(result) = shape(operand)num_bits(E') < num_bits(E)인 경우:rank(result) = R + 1.dim(result, i) = dim(operand, i)(모든0 <= i < R)dim(result, R) * num_bits(E') = num_bits(E).num_bits(E') > num_bits(E)인 경우:rank(result) = R - 1.dim(result, i) = dim(operand, i)(모든0 <= i < R)dim(operand, R - 1) * num_bits(E) = num_bits(E').
- (C2)
is_complex(operand) or is_complex(result)인 경우is_complex(operand) and is_complex(result)입니다.
예
// %operand: 0x0123456789ABCDEF
%result = "stablehlo.bitcast_convert"(%operand)< : >(te>nsorf64<) - t>ensor4xf16
// %result: [0xCDEF, 0x89AB, 0x4567, 0x0123] // little-endian representation
broadcast_in_dim
시맨틱스
operand 텐서의 데이터를 복제하여 입력 텐서의 차원 또는 순위를 확장하고 result 텐서를 생성합니다. 더 공식적으로 말하면 result[result_index] = operand[operand_index]입니다. 여기서 axes(operand)의 모든 d에 대해 다음이 적용됩니다.
dim(operand, d) = 1인 경우operand_index[d] = 0- 그 밖의 경우에는
operand_index[d] = result_index[broadcast_dimensions[d]]입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 양자화된 텐서 | (C1-C2), (C5-C6) |
| (I2) | broadcast_dimensions |
si64 유형의 1차원 텐서 상수 |
(C2-C6) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 양자화된 텐서 | (C1), (C3), (C5-C6) |
제약조건
- (C1)
element_type(result)은 다음 식으로 주어집니다.!is_per_axis_quantized(operand)인 경우element_type(operand)element_type(operand)(quantization_dimension(operand),scales(operand),zero_points(operand)이 각각quantization_dimension(result),scales(result),zero_points(result)와 다를 수 있음)
- (C2)
size(broadcast_dimensions) = rank(operand) - (C3)
0 <= broadcast_dimensions < rank(result). - (C4)
is_unique(broadcast_dimensions) - (C5)
axes(operand)의 모든d에 대해 다음을 실행합니다.dim(operand, d) = 1또는dim(operand, d) = dim(result, broadcast_dimensions[d]).
- (C6)
is_per_axis_quantized(result):quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)].dim(operand, quantization_dimension(operand)) = 1이면scales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result)))입니다.
예
// %operand: [
// [1, 2, 3]
// ]
%result = "stablehlo.broadcast_in_dim"(%operand) {
broadcast_dimensio<ns = arra>yi64: 2, 1
}< : (ten>sor>1x3xi32<) - tenso>r2x3x2xi32
// %result: [
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ],
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ]
// ]
케이스
시맨틱스
index 값에 따라 branches에서 정확히 하나의 함수를 실행한 결과를 생성합니다. 더 격식 있게 표현하면 다음과 같습니다.
result = selected_branch()
여기서 각 항목의 의미는 다음과 같습니다.
0 <= index < size(branches)인 경우selected_branch = branches[index]- 그 밖의 경우에는
selected_branch = branches[-1]입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | index |
si32 유형의 0차원 텐서 |
|
| (I2) | branches |
가변 함수 수 | (C1~C4) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서, 양자화된 텐서 또는 토큰 | (C4) |
제약조건
- (C1)
0 < size(branches). - (C2)
input_types(branches...) = [] - (C3)
same(output_types(branches...)). - (C4)
type(results...) = output_types(branches[0])
예
// %index: -1
// %result_branch0: [0, 0]
// %result_branch1: [1, 1]
%result0, %result1 = "stablehlo.case"(%index) ({
"stablehlo.return"(%result_branch0, %resul<t_bra>nch0) : <(tens>or2>xi64, tensor2xi64) - ()
}, {
"stablehlo.return"(%result_branc<h1, %>result_b<ranch>1) >: (tensor2xi64, <ten>sor>2xi64) -< ()
}>) : (ten<sori3>2) - (tensor2xi64, tensor2xi64)
// %result0: [1, 1]
// %result1: [1, 1]
cbrt
시맨틱스
operand 텐서에 대해 요소별 세제곱근 연산을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
rootn(x, 3) - 복소수의 경우 복소수 세제곱근입니다.
- 양자화된 유형:
dequantize_op_quantize(cbrt, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [0.0, 1.0, 8.0, 27.0]
%result = "stablehlo.cbrt"(%operand)< : (t>ens>or4xf64<) - t>ensor4xf64
// %result: [0.0, 1.0, 2.0, 3.0]
ceil
시맨틱스
operand 텐서의 요소별 올림을 실행하고 result 텐서를 생성합니다.
IEEE-754 사양의 roundToIntegralTowardPositive 작업을 구현합니다. 양자화된 유형의 경우 dequantize_op_quantize(ceil, operand, type(result))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [-0.8166, -0.2530, 0.2530, 0.8166, 2.0]
%result = "stablehlo.ceil"(%operand)< : (t>ens>or5xf32<) - t>ensor5xf32
// %result: [-0.0, -0.0, 1.0, 1.0, 2.0]
cholesky
시맨틱스
행렬 배치에 대한 Cholesky 분해를 계산합니다.
더 공식적으로 말하면 index_space(result)의 모든 i에 대해 result[i0, ..., iR-3, :, :]는 a[i0, ..., iR-3, :, :]의 촐레스키 분해이며, 하삼각(lower이 true인 경우) 또는 상삼각 (lower이 false인 경우) 행렬 형식입니다.
반대쪽 삼각형, 즉 엄격한 상부 삼각형 또는 엄격한 하부 삼각형의 출력 값은 구현에 따라 정의됩니다.
입력 행렬이 에르미트 양의 정부호 행렬이 아닌 i이 있으면 동작이 정의되지 않습니다.
양자화된 유형의 경우 dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | a |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1-C3) |
| (I2) | lower |
i1 유형의 상수 |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(a) = baseline_type(result). - (C2)
2 <= rank(a) - (C3)
dim(a, -2) = dim(a, -1).
예
// %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
}< : (ten>sor>3x3xf32<) - ten>sor3x3xf64
// %result: [
// [1.0, 0.0, 0.0],
// [2.0, 4.0, 0.0],
// [3.0, 5.0, 6.0]
// ]
고정하다
시맨틱스
operand 텐서의 모든 요소를 최소값과 최대값 사이에 고정하고 result 텐서를 생성합니다. 더 공식적으로는 result[result_index] =
minimum(maximum(operand[result_index], min_element), max_element)입니다. 여기서 min_element = rank(min) = 0 ? min[] : min[result_index], max_element = rank(max) = 0 ? max[] : max[result_index]입니다. 양자화된 유형의 경우 dequantize_op_quantize(clamp, min, operand, max, type(result))을 실행합니다.
복소수에 순서를 적용하면 놀라운 의미가 발생하므로 향후 이 작업에 대한 복소수 지원을 삭제할 계획입니다 (#560).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | min |
텐서 또는 텐서별 양자화 텐서 | (C1), (C3) |
| (I2) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1~C4) |
| (I3) | max |
텐서 또는 텐서별 양자화 텐서 | (C2), (C3) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C4) |
제약조건
- (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)
예
// %min: [5, 10, 15]
// %operand: [3, 13, 23]
// %max: [10, 15, 20]
%result = "stablehlo.clamp"(%min, %operand, %max)< : (t>ensor3xi<32, t>ensor3xi<32, t>ens>or3xi32<) - t>ensor3xi32
// %result: [5, 13, 20]
collective_broadcast
시맨틱스
StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 소스 프로세스의 operand 텐서 값을 타겟 프로세스로 전송하고 result 텐서를 생성합니다.
이 작업은 StableHLO 프로세스 그리드를 다음과 같이 정의된 process_groups로 분할합니다.
channel_id <= 0인 경우cross_replica(replica_groups)channel_id > 0인 경우cross_partition(replica_groups)
그 후 result@process는 다음과 같이 주어집니다.
process_groups[i]에 프로세스가 있는i가 있는 경우operand@process_groups[i, 0]- 그 밖의 경우에는
broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result))을 반환합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C3) |
| (I2) | replica_groups |
si64 유형의 1차원 텐서 상수의 가변 개수 |
(C1), (C2) |
| (I3) | channel_id |
si64 유형의 상수 |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C3) |
제약조건
- (C1)
is_unique(replica_groups). - (C2)
0 <= replica_groups < N(여기서N은 다음과 같이 정의됨)cross_replica이 사용된 경우num_replicascross_partition이 사용된 경우num_partitions
- (C3)
type(result) = type(operand).
예
// 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_grou<ps = den>se[[2, 1]<] : ten>sor1x2xi64,
channel_handle = #stablehlo.chan<nel_handlehandle = 0>, type = 0
} : (ten>sor>1x2xi64<) - ten>sor1x2xi64
// %result@(0, 0): [[0, 0]]
// %result@(1, 0): [[5, 6]]
// %result@(2, 0): [[5, 6]]
// %result@(3, 0): [[0, 0]]
collective_permute
시맨틱스
StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 소스 프로세스의 operand 텐서 값을 타겟 프로세스로 전송하고 result 텐서를 생성합니다.
이 작업은 StableHLO 프로세스 그리드를 다음과 같이 정의된 process_groups로 분할합니다.
channel_id <= 0인 경우cross_replica(source_target_pairs)channel_id > 0인 경우cross_partition(source_target_pairs)
그 후 result@process는 다음과 같이 주어집니다.
operand@process_groups[i, 0]:process_groups[i, 1] = process인i이 있는 경우- 그 밖의 경우에는
broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result))을 반환합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C5) |
| (I2) | source_target_pairs |
si64 유형의 2차원 텐서 상수 |
(C1~C4) |
| (I3) | channel_id |
si64 유형의 상수 |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (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, 여기서N은 다음과 같이 정의됩니다.cross_replica이 사용된 경우num_replicascross_partition이 사용된 경우num_partitions
- (C5)
type(result) = type(operand).
예
// 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_pai<rs = dense[[0, 1>], [1, 2]<] : ten>sor2x2xi64,
channel_handle = #stablehlo.chan<nel_handlehandle = 0>, type = 0
}< : (ten>sor>2x2xi64<) - ten>sor2x2xi64
//
// %result@(0, 0): [[0, 0], [0, 0]]
// %result@(1, 0): [[1, 2], [3, 4]]
// %result@(2, 0): [[5, 6], [7, 8]]
compare
시맨틱스
comparison_direction 및 compare_type에 따라 lhs 및 rhs 텐서의 요소별 비교를 수행하고 result 텐서를 생성합니다.
comparison_direction 및 compare_type 값은 다음과 같은 의미를 갖습니다.
불리언 및 정수 요소 유형의 경우:
EQ:lhs = rhs.NE:lhs != rhs.GE:lhs >= rhs.GT:lhs > rhs.LE:lhs <= rhs.LT:lhs < rhs.
compare_type = FLOAT가 있는 부동 소수점 요소 유형의 경우 op는 다음 IEEE-754 작업을 구현합니다.
EQ:compareQuietEqual.NE:compareQuietNotEqual.GE:compareQuietGreaterEqual.GT:compareQuietGreater.LE:compareQuietLessEqual.LT:compareQuietLess.
compare_type = TOTALORDER가 있는 부동 소수점 요소 유형의 경우 작업은 IEEE-754의 totalOrder 및 compareQuietEqual 작업의 조합을 사용합니다.
복잡한 요소 유형의 경우 제공된 comparison_direction 및 compare_type를 사용하여 (real, imag) 쌍의 사전식 비교가 실행됩니다.
복소수에 순서를 적용하면 놀라운 의미가 발생하므로 향후 comparison_direction이 GE, GT, LE 또는 LT인 경우 복소수 지원을 삭제할 계획입니다(#560).
양자화된 유형의 경우 dequantize_compare(lhs, rhs,
comparison_direction)를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
텐서 또는 텐서별 양자화 텐서 | (C1-C3) |
| (I2) | rhs |
텐서 또는 텐서별 양자화 텐서 | (C1-C2) |
| (I3) | comparison_direction |
EQ, NE, GE, GT, LE, LT의 enum |
|
| (I4) | compare_type |
FLOAT, TOTALORDER, SIGNED, UNSIGNED의 enum |
(C3) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
불리언 유형의 텐서 | (C2) |
제약조건
- (C1)
baseline_element_type(lhs) = baseline_element_type(rhs). - (C2)
shape(lhs) = shape(rhs) = shape(result) - (C3)
compare_type는 다음과 같이 정의됩니다.is_signed_integer(element_type(lhs))인 경우SIGNEDis_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs))인 경우UNSIGNEDis_float(element_type(lhs))인 경우FLOAT또는TOTALORDERis_complex(element_type(lhs))인 경우FLOAT
예
// %lhs: [1.0, 3.0]
// %rhs: [1.1, 2.9]
%result = "stablehlo.compare"(%lhs, %rhs) {
comparison_direction = <#stablehlocomparison_di>rection LT,
compare_type = <#stablehlocomparison_>type FLOAT
}< : (t>ensor2xf<32, t>ens>or2xf32<) - >tensor2xi1
// %result: [true, false]
복잡한
시맨틱스
실수 값과 허수 값의 쌍인 lhs 및 rhs에서 복소수 값으로 요소별 변환을 실행하고 result 텐서를 생성합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
f32 또는 f64 유형의 텐서 |
(C1-C3) |
| (I2) | rhs |
f32 또는 f64 유형의 텐서 |
(C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
복합 유형의 텐서 | (C2), (C3) |
제약조건
- (C1)
type(lhs) = type(rhs). - (C2)
shape(result) = shape(lhs) - (C3)
element_type(result)의 유형은complex<E>이며 여기서E = element_type(lhs)입니다.
예
// %lhs: [1.0, 3.0]
// %rhs: [2.0, 4.0]
%result = "stablehlo.complex"(%lhs, %rhs)< : (t>ensor2xf<64, t>ens>or2xf64<) - tenso<r2x>>complexf64
// %result: [(1.0, 2.0), (3.0, 4.0)]
복합
시맨틱스
inputs 및 composite_attributes를 사용하고 results를 생성하는 다른 StableHLO 작업으로 구성된 작업을 캡슐화합니다. 작업의 의미는 decomposition 속성으로 구현됩니다. composite 작업은 프로그램 의미 체계를 변경하지 않고 분해로 대체할 수 있습니다. 분해를 인라인으로 처리해도 동일한 op 시맨틱이 제공되지 않는 경우 custom_call를 사용하는 것이 좋습니다.
version 필드 (기본값은 0)는 컴포지션의 의미가 변경되는 시점을 나타내는 데 사용됩니다.
입력
| 라벨 | 이름 | 유형 |
|---|---|---|
| (I1) | inputs |
가변 개수의 값 |
| (I2) | name |
string 유형의 상수 |
| (I3) | composite_attributes |
속성 사전 |
| (I4) | decomposition |
string 유형의 상수 |
| (I5) | version |
si32 유형의 상수 |
출력
| 이름 | 유형 |
|---|---|
results |
가변 개수의 값 |
제약조건
- (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)
예
%results = "stablehlo.composite"(%input0, %input1) {
name = "my_namespace.my_op",
composite_attributes = {
my_attribute = "my_value"
},
decomposition = @my_op,
< ve>rsion = <1 :> i3>2
} : (<ten>sorf32, tensorf32) - tensorf32
이어붙이기
시맨틱스
지정된 인수와 동일한 순서로 dimension 차원을 따라 inputs을 연결하고 result 텐서를 생성합니다. 더 공식적으로는 result[i0, ..., id, ..., iR-1] = inputs[k][i0, ..., kd, ..., iR-1]입니다. 여기서 각 항목의 의미는 다음과 같습니다.
id = d0 + ... + dk-1 + kd.d은dimension과 같고d0, ... 은inputs의d번째 차원 크기입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | inputs |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C1-C6) |
| (I2) | dimension |
si64 유형의 상수 |
(C2), (C4), (C6) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C5~C6) |
제약조건
- (C1)
same(element_type(inputs...)). - (C2)
dim(inputs..., dimension)를 제외한same(shape(inputs...)) - (C3)
0 < size(inputs). - (C4)
0 <= dimension < rank(inputs[0]) - (C5)
element_type(result) = element_type(inputs[0]). - (C6)
shape(result) = shape(inputs[0])(다음 제외)dim(result, dimension) = dim(inputs[0], dimension) + ....
예
// %input0: [[1, 2], [3, 4], [5, 6]]
// %input1: [[7, 8]]
%result = "stablehlo.concatenate"(%input0, %input1) {
dimension = 0 : i64
}< : (ten>sor3x2xi<64, ten>sor>1x2xi64<) - ten>sor4x2xi64
// %result: [[1, 2], [3, 4], [5, 6], [7, 8]]
상수
시맨틱스
상수 value에서 output 텐서를 생성합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | value |
상수 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
output |
텐서 또는 양자화된 텐서 | (C1) |
제약조건
- (C1)
type(value) = type(output).
예
%output = "stablehlo.constant"() {
val<ue = dense[[0.0, 1.0], [>2.0, 3.0]<] : ten>sor2x2xf3>2
} : (<) - ten>sor2x2xf32
// %output: [[0.0, 1.0], [2.0, 3.0]]
전환
시맨틱스
operand 텐서에서 한 요소 유형을 다른 요소 유형으로 요소별로 변환하고 result 텐서를 생성합니다.
boolean-to-any-supported-type 변환의 경우 값 false는 0으로 변환되고 값 true는 1로 변환됩니다. any-supported-type-to-boolean 변환의 경우 0 값은 false로 변환되고 0이 아닌 값은 true로 변환됩니다. 복잡한 유형의 경우 아래를 참고하세요.
정수-정수, 정수-부동 소수점 또는 부동 소수점-부동 소수점 변환의 경우 소스 값을 대상 유형으로 정확하게 표현할 수 있으면 결과 값은 정확한 표현입니다. 그렇지 않으면 동작은 미정입니다(#180).
floating-point-to-integer 변환하는 경우 소수 부분이 잘립니다. 잘린 값을 대상 유형으로 나타낼 수 없는 경우 동작은 미정입니다 (#180).
복소수-복소수 변환은 실수부와 허수부를 변환하는 부동 소수점-부동 소수점 변환과 동일한 동작을 따릅니다.
complex-to-any-other-type 및 any-other-type-to-complex 변환의 경우 소스 허수 값이 무시되거나 대상 허수 값이 0으로 설정됩니다. 실수부의 변환은 부동 소수점 변환을 따릅니다.
원칙적으로 이 작업은 역양자화 (양자화된 텐서에서 일반 텐서로 변환), 양자화 (일반 텐서에서 양자화된 텐서로 변환), 재양자화 (양자화된 텐서 간 변환)를 표현할 수 있지만 현재는 첫 번째 사용 사례의 경우 uniform_dequantize, 두 번째 및 세 번째 사용 사례의 경우 uniform_quantize와 같은 전용 작업이 있습니다. 향후 이 두 작업은 convert로 병합될 수 있습니다 (#1576).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 | (C1) |
제약조건
- (C1)
shape(operand) = shape(result).
예
// %operand: [-1, 0, 1]
%result = "stablehlo.convert"(%operand)< : (t>ens>or3xi64<) - tenso<r3x>>complexf64
// %result: [(-1.0, 0.0), (0.0, 0.0), (1.0, 0.0)]
컨볼루션
시맨틱스
lhs의 창과 rhs의 슬라이스 간의 내적을 계산하고 result를 생성합니다. 다음 다이어그램은 구체적인 예를 사용하여 lhs 및 rhs에서 result의 요소가 계산되는 방식을 보여줍니다.
좀 더 공식적으로 lhs의 창을 표현할 수 있도록 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).
이 프레임 재설정은 다음 도우미 함수를 사용합니다.
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]).j[d] = i[permutation[d]]조건을 충족하는permute([j0, j1, ..., jR-1], permutation) = [i0, i1, ..., iR-1]
feature_group_count = 1 및 batch_group_count = 1인 경우 index_space(dim(result, output_spatial_dimensions...))의 모든 output_spatial_index에 대해 다음을 충족하는 result[result_shape(:, output_spatial_index, :)] = dot_product입니다.
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]). 이 기능은 사용되지 않는 것으로 보이므로 향후 삭제할 계획입니다 (#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]).
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).
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).
양자화된 유형의 경우 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))를 실행합니다.
하이브리드 양자화 유형의 경우 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)를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
텐서 또는 텐서별 양자화 텐서 | (C1), (C10~C11), (C14), (C25), (C27~C28), (C31~C32), (C34) |
| (I2) | rhs |
텐서 또는 양자화된 텐서 | (C1), (C14~C16), (C25), (C27~C29), (C31~C34) |
| (I3) | window_strides |
si64 유형의 1차원 텐서 상수 |
(C2~C3), (C25) |
| (I4) | padding |
si64 유형의 2차원 텐서 상수 |
(C4), (C25) |
| (I5) | lhs_dilation |
si64 유형의 1차원 텐서 상수 |
(C5~C6), (C25) |
| (I6) | rhs_dilation |
si64 유형의 1차원 텐서 상수 |
(C7~C8), (C25) |
| (I7) | window_reversal |
i1 유형의 1차원 텐서 상수 |
(C9) |
| (I8) | input_batch_dimension |
si64 유형의 상수 |
(C10), (C13), (C25) |
| (I9) | input_feature_dimension |
si64 유형의 상수 |
(C11), (C13-C14) |
| (I10) | input_spatial_dimensions |
si64 유형의 1차원 텐서 상수 |
(C12), (C13), (C25) |
| (I11) | kernel_input_feature_dimension |
si64 유형의 상수 |
(C14), (C18) |
| (I12) | kernel_output_feature_dimension |
si64 유형의 상수 |
(C15~C16), (C18), (C25), (C29) |
| (I13) | kernel_spatial_dimensions |
si64 유형의 1차원 텐서 상수 |
(C17~C18), (C25) |
| (I14) | output_batch_dimension |
si64 유형의 상수 |
(C20), (C25) |
| (I15) | output_feature_dimension |
si64 유형의 상수 |
(C20), (C25), (C30) |
| (I16) | output_spatial_dimensions |
si64 유형의 1차원 텐서 상수 |
(C19~C20), (C25) |
| (I17) | feature_group_count |
si64 유형의 상수 |
(C11), (C14), (C16), (C21), (C23) |
| (I18) | batch_group_count |
si64 유형의 상수 |
(C10), (C15), (C22), (C23), (C25) |
| (I19) | precision_config |
DEFAULT, HIGH, HIGHEST의 가변 개수 열거형 |
(C24) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 양자화된 텐서 | (C25~C28), (C30), (C32~34) |
제약조건
- (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)
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)
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)
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)은 다음과 같이 정의됩니다.result_dim = output_batch_dimension인 경우dim(lhs, input_batch_dimension) / batch_group_countresult_dim = output_feature_dimension인 경우dim(rhs, kernel_output_feature_dimension)- 그렇지 않으면
num_windows입니다. 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. - 작업에서 양자화되지 않은 텐서를 사용하는 경우 다음을 충족해야 합니다.
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result).
- (C27)
- 작업에서 양자화된 텐서를 사용하는 경우 다음을 충족해야 합니다.
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs). - (C29)
is_per_axis_quantized(rhs)인 경우quantization_dimension(rhs) = kernel_output_feature_dimension입니다. - (C30)
is_per_axis_quantized(result)이면quantization_dimension(result) = output_feature_dimension입니다. is_quantized(lhs)인 경우:- (C31)
storage_type(lhs) = storage_type(rhs) - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result). - (C33)
is_per_tensor_quantized(rhs)이면is_per_tensor_quantized(result)입니다. !is_quantized(lhs)인 경우:- (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
- (C28)
예
// %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_strid<es = arra>yi64: 4, 4,
paddi<n>g = dense<0 : ten>sor2x2xi64,
lhs_dilati<on = arra>yi64: 2, 2,
rhs_dilati<on = arra>yi64: 1, 1,
window_revers<al = arrayi1: fa>lse, false,
// In the StableHLO dialect, dimension numbers are encoded vi<a:
// `[input >dim<ensions]x[kernel >di>mensions]-[output dimensions]`.
// "b" is batch dimension, "f" is feature dimension,
// "i" is input feature dimension, "o" is output feature dimension,
// "0/1/etc" a<re spatial dimensions.
d>imension_num>bers = #stablehlo.conv[b, 0, 1, f]x[0, 1, i, o]-[b, 0, 1, f],
batch_group_count = 1 : i64,
fea<ture_group_count >= 1 : i64,
< precision_config> = [#stablehl<oprecision >DEFAULT,< #stablehlo>pre>cision <DEFAULT]
} >: (tensor1x4x4x1xi64, tensor3x3x1x1xi64) - tensor1x2x2x1xi64
// %result: [[
// [[10], [26]],
// [[46], [62]]
// ]]
코사인
시맨틱스
operand 텐서에 요소별 코사인 연산을 수행하여 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
cos - 복소수의 경우 복소수 코사인입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(cosine, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.cosine"(%operand)< : (ten>sor>2x2xf32<) - ten>sor2x2xf32
// %result: [[1.0, 0.0], [-1.0, 0.0]]
count_leading_zeros
시맨틱스
operand 텐서에서 선행하는 0비트의 수를 요소별로 계산하고 result 텐서를 생성합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
정수형 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수형 텐서 | (C1) |
제약조건
- (C1)
type(operand) = type(result).
예
// %operand: [[0, 1], [128, -1]]
%result = "stablehlo.count_leading_zeros"(%operand)< : (ten>sor>2x2xi64<) - ten>sor2x2xi64
// %result: [[64, 63], [56, 0]]
custom_call
시맨틱스
inputs 및 called_computations를 가져와 results를 생성하는 구현 정의 작업 call_target_name를 캡슐화합니다. has_side_effect, backend_config, api_version은 구현 정의 메타데이터를 추가로 제공하는 데 사용할 수 있습니다.
현재 이 작업에는 XLA 컴파일러에서 상응하는 작업의 유기적 진화를 반영하는 상당히 체계적이지 않은 메타데이터 모음이 포함되어 있습니다. 향후 이 메타데이터를 통합할 계획입니다(#741).
입력
| 라벨 | 이름 | 유형 |
|---|---|---|
| (I1) | inputs |
가변 개수의 값 |
| (I2) | call_target_name |
string 유형의 상수 |
| (I3) | has_side_effect |
i1 유형의 상수 |
| (I4) | backend_config |
string 유형의 상수 또는 속성 사전 |
| (I5) | api_version |
si32 유형의 상수 |
| (I6) | called_computations |
string 유형의 가변 상수 수 |
| (I7) | output_operand_aliases |
출력 및 피연산자에서 별칭 지정 부분 지정 |
출력
| 이름 | 유형 |
|---|---|
results |
가변 개수의 값 |
(XLA GPU 지원) 특수 custom_call 타겟
buffer 유형과 관련된 세 가지 특수 call_target_name가 있습니다. CreateBuffer는 초기화되지 않은 buffer를 만들고, Pin는 초기화된 buffer를 만들며, Unpin는 buffer를 할당 해제하고 buffer의 콘텐츠를 반환합니다.
%uninitialized_buffer = "stablehlo.custom_call"() {
call_target_name = "CreateBuffer",
api_version> = 4 : <i32,
>} : () - memref4xf64
%initialized_buffer = "stablehlo.custom_call"(%init_value) {
call_target_name = "Pin&quo<t;,
> ap>i_versi<on = >4 : i32,
} : (tensor4xf64) - memref4xf64
%dealloc_buffer = "stablehlo.custom_call"(%initialized_buffer) {
call_target_na<me = >&qu>ot;Unpi<n&quo>t;,
api_version = 4 : i32,
} : (memref4xf64) - tensor4xf64
별칭
일부 custom_call 작업은 출력이 메모리를 공유하고 피연산자가 메모리를 공유해야 할 수 있습니다. 이는 output_operand_aliases를 통해 표현할 수 있습니다. 별칭 쌍 표현식은 출력 부분을 나타내는 출력 튜플 색인 목록과 피연산자 부분을 나타내는 피연산자 튜플 색인 목록과 함께 operand_index로 구성됩니다. 해당 유형이 tuple 유형이 아닌 경우 출력 또는 피연산자 튜플 인덱스 목록은 비어 있으며, 임의로 중첩된 튜플 유형의 경우 임의로 길 수 있습니다. 이는 XLA 별칭 표현과 유사합니다.
별칭 쌍의 출력 부분과 입력 부분은 유형이 동일해야 합니다. CreateBuffer, Pin, Unpin 호출이 아닌 custom_call 작업의 경우 buffer 피연산자는 최대 하나의 별칭 쌍에 표시될 수 있으며 buffer 출력은 하나의 별칭 쌍에 표시되어야 합니다.
예
%results = "stablehlo.custom_call"(%input0) {
call_target_name = "foo",
has_side_effect = false,
backend_config = {bar = 42 : i32},
api_version = 4 : i32,
called_computations <= [>@fo>o]
} : <(te>nsorf64) - tensorf64
%updated_buffer = "stablehlo.custom_call"(%buffer) {
call_target_name = "Update",
api_version = 4 : i32,
output_operand_aliases< = [
#stablehlo.output_operand_aliasoutput_tuple_indices = [],
operand_ind>ex = 0,
< oper>and>_tuple_<indic>es = []]
} : (memref4xf64) - memref4xf64
나누기
시맨틱스
피제수 lhs와 제수 rhs 텐서의 요소별 나눗셈을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 정수의 경우: 소수 부분이 삭제된 대수적 몫을 생성하는 정수 나눗셈입니다.
- 부동 소수점의 경우: IEEE-754의
division - 복소수의 경우 복소수 나눗셈입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(divide, lhs, rhs, type(result)).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I2) | rhs |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).
예
// %lhs: [17.1, -17.1, 17.1, -17.1]
// %rhs: [3.0, 3.0, -3.0, -3.0]
%result = "stablehlo.divide"(%lhs, %rhs)< : (t>ensor4xf<32, t>ens>or4xf32<) - t>ensor4xf32
// %result: [5.66666651, -5.66666651, -5.66666651, 5.66666651]
dot_general
시맨틱스
lhs 슬라이스와 rhs 슬라이스 간의 내적을 계산하고 result 텐서를 생성합니다.
더 공식적으로는 result[result_index] = dot_product입니다. 여기서
lhs_result_dimensions = [d for d in axes(lhs) and d not in lhs_batching_dimensions and d not in lhs_contracting_dimensions].rhs_result_dimensions = [d for d in axes(rhs) and d not in rhs_batching_dimensions and d not in rhs_contracting_dimensions].result_batching_index + result_lhs_index + result_rhs_index = result_index여기서size(result_batching_index) = size(lhs_batching_dimensions),size(result_lhs_index) = size(lhs_result_dimensions)및size(result_rhs_index) = size(rhs_result_dimensions)입니다.transposed_lhs = transpose(lhs, lhs_batching_dimensions + lhs_result_dimensions + lhs_contracting_dimensions).transposed_lhs_slice = slice(transposed_lhs, result_batching_index + result_lhs_index + [:, ..., :]).reshaped_lhs_slice = reshape(transposed_lhs_slice, dims(lhs, lhs_contracting_dimensions)).transposed_rhs = transpose(rhs, rhs_batching_dimensions + rhs_result_dimensions + rhs_contracting_dimensions).transposed_rhs_slice = slice(transposed_rhs, result_batching_index + result_rhs_index + [:, ..., :]).reshaped_rhs_slice = reshape(transposed_rhs_slice, dims(rhs, rhs_contracting_dimensions)).dot_product = reduce( inputs=[multiply(reshaped_lhs_slice, reshaped_rhs_slice)], init_values=[constant(0, element_type(result))], dimensions=range(size(lhs_contracting_dimensions)), body=lambda x, y: add(x, y)).
양자화된 유형의 경우 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))를 실행합니다.
하이브리드 양자화 유형의 경우 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는 가속기 백엔드에서 계산의 속도와 정확도 간의 균형을 제어합니다. 다음 중 하나일 수 있습니다 (현재 이러한 enum 값의 의미는 명확하게 명시되어 있지 않지만 #755에서 이 문제를 해결할 예정임).
DEFAULT: 가장 빠른 계산이지만 원래 숫자에 대한 근사치가 가장 정확하지 않습니다.HIGH: 계산이 느리지만 원래 숫자에 더 정확하게 근사합니다.HIGHEST: 가장 느린 계산이지만 원래 숫자에 가장 정확하게 근사합니다.
DotAlgorithm는 점 연산을 구현하는 데 사용되는 알고리즘의 기본 속성을 정의하며, 이는 정밀도도 정의합니다. 알고리즘 속성 필드가 설정된 경우 precision_config는 DEFAULT이어야 합니다. DotAlgorithms에는 기본값이 없습니다. 기본 매개변수는 구현에 따라 정의되기 때문입니다. 따라서 모든 점 알고리즘 필드를 None로 설정하여 빈 점 알고리즘을 지정할 수 있으며, 이 경우 precision_config 값이 대신 사용됩니다.
DotAlgorithm 필드에는 다음이 포함됩니다.
lhs_precision_type및rhs_precision_type: 작업의 좌항과 우항이 반올림되는 정밀도입니다. 정밀도 유형은 입력 및 출력의 저장 유형과 독립적입니다.accumulation_type누적에 사용되는 정밀도입니다.lhs_component_count,rhs_component_count,num_primitive_operations는 좌변 또는 우변을 여러 구성요소로 분해하고 이러한 값에 여러 '기본' 점 연산을 실행하는 알고리즘을 실행할 때 적용됩니다. 이는 일반적으로 더 높은 정밀도를 에뮬레이션하기 위한 것입니다 (예: 더 높은 정밀도 계산을 위해 bfloat16 인공지능 데이터 유형 활용: bf16_6x tf32_3x 등). 분해가 없는 알고리즘의 경우 이러한 값은1으로 설정해야 합니다.allow_imprecise_accumulation: 일부 단계 (예:CUBLASLT_MATMUL_DESC_FAST_ACCUM)에서 낮은 정밀도로 누적이 허용되는지 지정합니다.
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}
어떤 조합이 지원되는지는 구현에 달려 있습니다. 일반적으로 각 알고리즘이 StableHLO의 소비자에 의해 각 가속기 유형에서 지원된다고 보장되지는 않습니다. 지정된 알고리즘이 지원되지 않으면 대체 알고리즘으로 대체하는 대신 오류가 발생해야 합니다. StableHLO 검증은 최선을 다해 검증하여 모든 하드웨어에서 지원되지 않는 것으로 알려진 알고리즘을 방지합니다.
지원되는 알고리즘 값은 xla_data.proto > Algorithm을 참고하세요. 티켓 #2483에는 백엔드에서 지원하는 알고리즘에 관한 중앙 집중식 문서를 만들 계획이 나와 있습니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
텐서 또는 텐서별 양자화 텐서 | (C5~C6), (C9~C10), (C12~C14), (C17~C18), (C20) |
| (I2) | rhs |
텐서 또는 양자화된 텐서 | (C7~C10), (C12~C20) |
| (I3) | lhs_batching_dimensions |
si64 유형의 1차원 텐서 상수 |
(C1), (C3), (C5), (C9), (C12) |
| (I4) | rhs_batching_dimensions |
si64 유형의 1차원 텐서 상수 |
(C1), (C4), (C7), (C9) |
| (I5) | lhs_contracting_dimensions |
si64 유형의 1차원 텐서 상수 |
(C2), (C3), (C6), (C10) |
| (I6) | rhs_contracting_dimensions |
si64 유형의 1차원 텐서 상수 |
(C2), (C4), (C8), (C10), (C16) |
| (I7) | precision_config |
DEFAULT, HIGH, HIGHEST의 가변 개수 열거형 |
(C11), (C21) |
| (I8) | lhs_precision_type |
FloatType 또는 TensorFloat32 | (C21) |
| (I9) | rhs_precision_type |
FloatType 또는 TensorFloat32 | (C21) |
| (I10) | accumulation_type |
FloatType 또는 TensorFloat32 | (C21) |
| (I11) | lhs_component_count |
si32 유형의 상수 |
(C21), (C22) |
| (I12) | rhs_component_count |
si32 유형의 상수 |
(C21), (C23) |
| (I13) | num_primitive_operations |
si32 유형의 상수 |
(C21), (C24) |
| (I14) | allow_imprecise_accumulation |
bool 유형의 상수 |
(C21) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 양자화된 텐서 | (C12), (C14), (C18~C20) |
제약조건
- (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). - 작업에서 양자화되지 않은 텐서를 사용하는 경우 다음을 충족해야 합니다.
- (C13)
element_type(lhs) = element_type(rhs).
- (C13)
- 작업에서 양자화된 텐서를 사용하는 경우 다음을 충족해야 합니다.
- (C14)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs) - (C15)
zero_points(rhs) = 0 - (C16)
is_per_axis_quantized(rhs)인 경우quantization_dimension(rhs)이rhs_contracting_dimensions에 없습니다. is_quantized(lhs)인 경우:- (C17)
storage_type(lhs) = storage_type(rhs). - (C18)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result). - (C19)
is_per_tensor_quantized(rhs)이면is_per_tensor_quantized(result)입니다. !is_quantized(lhs)인 경우:- (C20)
element_type(lhs) = expressed_type(rhs) = element_type(result).
- (C14)
!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)
예
// %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 = #sta<blehlo.dot
lhs_batching_dimensions = [0],
rhs_batching_dimensions = [0],
lhs_contracting_dimensions = [2],
rhs_contracting_dimension>s = [1]
,
precision_config = [<#stablehloprecisi>on DEFAULT, <#stablehloprecisi>on 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
}< : (tenso>r2x2x2xi<64, tenso>r2x>2x2xi64<) - tenso>r2x2x2xi64
// %result: [
// [[1, 2],
// [3, 4]],
// [[5, 6],
// [7, 8]]
// ]
dynamic_broadcast_in_dim
시맨틱스
이 작업은 broadcast_in_dim 작업과 기능적으로 동일하지만 결과 모양은 output_dimensions를 통해 동적으로 지정됩니다.
이 작업은 측정기준의 확장 동작에 관한 정적 지식을 표현하기 위해 선택적 속성 known_expanding_dimensions, known_nonexpanding_dimensions도 허용합니다.
지정하지 않으면 모든 측정기준이 확장될 수 있는 것으로 간주됩니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 양자화된 텐서 | (C1~C2), (C5~C6), (C9) |
| (I2) | output_dimensions |
정수 유형의 1차원 텐서 | (C7) |
| (I3) | broadcast_dimensions |
정수 유형의 1차원 상수 텐서 | (C2-C6) |
| (I4) | known_expanding_dimensions |
정수 유형의 1차원 상수 텐서 | (C8~C9) |
| (I5) | known_nonexpanding_dimensions |
정수 유형의 1차원 상수 텐서 | (C8~C9) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 양자화된 텐서 | (C1), (C3), (C5~C7) |
제약조건
- (C1)
element_type(result)은 다음 식으로 주어집니다.!is_per_axis_quantized(operand)인 경우element_type(operand)element_type(operand)(quantization_dimension(operand),scales(operand),zero_points(operand)이 각각quantization_dimension(result),scales(result),zero_points(result)와 다를 수 있음)
- (C2)
size(broadcast_dimensions) = rank(operand) - (C3)
0 <= broadcast_dimensions < rank(result). - (C4)
is_unique(broadcast_dimensions) - (C5)
axes(operand)의 모든d에 대해 다음을 실행합니다.dim(operand, d) = 1또는dim(operand, d) = dim(result, broadcast_dimensions[d]).
- (C6)
is_per_axis_quantized(result):quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)].dim(operand, quantization_dimension(operand)) = 1이면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).
예
// %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_dimensio<ns = arra>yi64: 2, 1,
known_expanding_dimensio<ns = a>rrayi64: 0,
known_nonexpanding_dimensio<ns = a>rrayi64: 1
}< : (ten>sor1x3xi<64, t>ens>or3xi64<) - tenso>r2x3x2xi64
// %result: [
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ],
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ]
// ]
dynamic_conv
시맨틱스
이 작업은 기능적으로 컨볼루션 작업과 동일하지만 패딩은 padding를 통해 동적으로 지정됩니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
텐서 또는 텐서별 양자화 텐서 | (C1), (C10~C11), (C14), (C25), (C26~C27), (C30~C31), (C33) |
| (I2) | rhs |
텐서 또는 양자화된 텐서 | (C1), (C14~C16), (C26~C28), (C30~C33) |
| (I3) | padding |
정수 유형의 2차원 텐서 | (C4) |
| (I4) | window_strides |
si64 유형의 1차원 텐서 상수 |
(C2-C3) |
| (I5) | lhs_dilation |
si64 유형의 1차원 텐서 상수 |
(C5~C6) |
| (I6) | rhs_dilation |
si64 유형의 1차원 텐서 상수 |
(C7~C8) |
| (I7) | window_reversal |
i1 유형의 1차원 텐서 상수 |
(C9) |
| (I8) | input_batch_dimension |
si64 유형의 상수 |
(C10), (C13) |
| (I9) | input_feature_dimension |
si64 유형의 상수 |
(C11), (C13-C14) |
| (I10) | input_spatial_dimensions |
si64 유형의 1차원 텐서 상수 |
(C12), (C13) |
| (I11) | kernel_input_feature_dimension |
si64 유형의 상수 |
(C14), (C18) |
| (I12) | kernel_output_feature_dimension |
si64 유형의 상수 |
(C15~C16), (C18), (C28) |
| (I13) | kernel_spatial_dimensions |
si64 유형의 1차원 텐서 상수 |
(C17~C18) |
| (I14) | output_batch_dimension |
si64 유형의 상수 |
(C20) |
| (I15) | output_feature_dimension |
si64 유형의 상수 |
(C20), (C29) |
| (I16) | output_spatial_dimensions |
si64 유형의 1차원 텐서 상수 |
(C19~C20) |
| (I17) | feature_group_count |
si64 유형의 상수 |
(C11), (C14), (C16), (C21), (C23) |
| (I18) | batch_group_count |
si64 유형의 상수 |
(C10), (C15), (C22), (C23) |
| (I19) | precision_config |
DEFAULT, HIGH, HIGHEST의 가변 개수 열거형 |
(C24) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 양자화된 텐서 | (C25~C27), (C29), (C31~C33) |
제약조건
- (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)
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)
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)
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)은 다음과 같이 정의됩니다.result_dim = output_batch_dimension인 경우dim(lhs, input_batch_dimension) / batch_group_countresult_dim = output_feature_dimension인 경우dim(rhs, kernel_output_feature_dimension)- 그렇지 않으면
num_windows입니다. 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. - 작업에서 양자화되지 않은 텐서를 사용하는 경우 다음을 충족해야 합니다.
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result).
- (C27)
- 작업에서 양자화된 텐서를 사용하는 경우 다음을 충족해야 합니다.
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs). - (C29)
is_per_axis_quantized(rhs)인 경우quantization_dimension(rhs) = kernel_output_feature_dimension입니다. - (C30)
is_per_axis_quantized(result)이면quantization_dimension(result) = output_feature_dimension입니다. is_quantized(lhs)인 경우:- (C31)
storage_type(lhs) = storage_type(rhs) - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result). - (C33)
is_per_tensor_quantized(rhs)이면is_per_tensor_quantized(result)입니다. !is_quantized(lhs)인 경우:- (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
- (C28)
예
// %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_strid<es = arra>yi64: 4, 4,
lhs_dilati<on = arra>yi64: 2, 2,
rhs_dilati<on = arra>yi64: 1, 1,
window_revers<al = arrayi1: fa>lse, false,
dimension_numbers = #stab<lehlo.convraw
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 = [<#stablehloprecisi>on DEFAULT, <#stablehloprecisi>on DEFAULT]
}< : (tensor1>x4x4x1xi<64, tensor3>x3x1x1xi<64, ten>sor>2x2xi64<) - tensor1>x2x2x1xi64
// %result: [[
// [[1], [5]],
// [[10], [14]]
// ]]
dynamic_gather
시맨틱스
이 작업은 slice_sizes이 값으로 동적으로 지정된다는 점을 제외하면 gather 작업과 기능적으로 동일합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1), (C7), (C10~C12), (C14) |
| (I2) | start_indices |
정수형 텐서 | (C2), (C3), (C13) |
| (I3) | slice_sizes |
정수 유형의 1차원 텐서 | (C8), (C11-C13) |
| (I4) | offset_dims |
si64 유형의 1차원 텐서 상수 |
(C1), (C4~C5), (C13) |
| (I5) | collapsed_slice_dims |
si64 유형의 1차원 텐서 상수 |
(C1), (C6~C8), (C13) |
| (I6) | start_index_map |
si64 유형의 1차원 텐서 상수 |
(C3), (C9), (C10) |
| (I7) | index_vector_dim |
si64 유형의 상수 |
(C2), (C3), (C13) |
| (I8) | indices_are_sorted |
i1 유형의 상수 |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C5), (C13-C14) |
제약조건
- (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)각 항목의 의미는 다음과 같습니다.batch_dim_sizes = shape(start_indices)와 유사하지만index_vector_dim에 해당하는start_indices의 측정기준 크기는 포함되지 않습니다.collapsed_slice_dims에 해당하는slice_sizes의 측정기준 크기는 포함되지 않는다는 점을 제외하면offset_dim_sizes = shape(slice_sizes)와 같습니다.combine는batch_dims에 해당하는 축에batch_dim_sizes를 배치하고offset_dims에 해당하는 축에offset_dim_sizes를 배치합니다.
- (C14)
element_type(operand) = element_type(result)
예
// %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 = #stable<hlo.gather
offset_dims = [2, 3],
collapsed_slice_dims = [0],
start_index_map = [1, 0],
index_vect>or_dim = 2,
indices_are_sorted = false
}< : (tenso>r3x4x2xi<64, tenso>r2x3x2xi<64, t>ens>or3xi64<) - tensor2>x3x2x2xi64
// %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
시맨틱스
이 작업은 기능적으로 iota 작업과 동일하지만 결과 모양은 output_shape를 통해 동적으로 지정됩니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | output_shape |
정수 유형의 1차원 텐서 | (C1), (C2) |
| (I2) | iota_dimension |
si64 |
(C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C2) |
제약조건
- (C1)
0 <= iota_dimension < size(output_shape). - (C2)
rank(result) = size(output_shape)
예
%output_shape = stablehlo.constant dense<[4, 5]> : tensor<2xi64>
%result = "stablehlo.dynamic_iota"(%output_shape) {
iota_dimension = 0 : i64
}< : (t>ens>or2xi64<) - ten>sor4x5xi64
// %result: [
// [0, 0, 0, 0, 0],
// [1, 1, 1, 1, 1],
// [2, 2, 2, 2, 2],
// [3, 3, 3, 3, 3]
// ]
dynamic_pad
시맨틱스
이 작업은 pad 작업과 기능적으로 동일하지만 edge_padding_low, edge_padding_high, interior_padding이 값으로 동적으로 지정됩니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1), (C2), (C4) |
| (I2) | padding_value |
0차원 텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I3) | edge_padding_low |
정수 유형의 1차원 텐서 | (C1), (C4) |
| (I4) | edge_padding_high |
정수 유형의 1차원 텐서 | (C1), (C4) |
| (I5) | interior_padding |
정수 유형의 1차원 텐서 | (C2-C4) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C3~C6) |
제약조건
- (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
예
// %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
)< : (ten>sor2x3xi<64,> tensori<64, t>ensor2xi<64, t>ensor2xi<64, t>ens>or2xi64<) - ten>sor5x9xi64
// %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
시맨틱스
이 작업은 기능적으로 reshape 작업과 동일하지만 결과 모양은 output_shape를 통해 동적으로 지정됩니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 양자화된 텐서 | (C1-C3) |
| (I2) | output_shape |
정수 유형의 1차원 텐서 | (C4) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 양자화된 텐서 | (C1~C4) |
제약조건
- (C1)
element_type(result)은 다음 식으로 주어집니다.!is_per_axis_quantized(operand)인 경우element_type(operand)element_type(operand)와 동일합니다. 단,quantization_dimension(operand)및quantization_dimension(result)는 다를 수 있습니다.
- (C2)
size(operand) = size(result) - (C3)
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)
예
// %operand: [[1, 2, 3], [4, 5, 6]]
// %output_shape: [3, 2]
%result = "stablehlo.dynamic_reshape"(%operand, %output_shape)< : (ten>sor2x3xi<64, t>ens>or2xi64<) - ten>sor3x2xi64
// %result: [[1, 2], [3, 4], [5, 6]]
dynamic_slice
시맨틱스
동적으로 계산된 시작 색인을 사용하여 operand에서 슬라이스를 추출하고 result 텐서를 생성합니다. start_indices에는 조정될 수 있는 각 차원의 슬라이스 시작 색인이 포함되고 slice_sizes에는 각 차원의 슬라이스 크기가 포함됩니다. 더 격식 있게 표현하면 다음과 같습니다.
result[result_index] = operand[operand_index]
adjusted_start_indices = clamp(0, start_indices, shape(operand) - slice_sizes).operand_index = adjusted_start_indices + result_index.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1), (C2), (C4) |
| (I2) | start_indices |
정수 유형의 0차원 텐서 가변 개수 | (C2), (C3) |
| (I3) | slice_sizes |
si64 유형의 1차원 텐서 상수 |
(C2), (C4), (C5) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C1), (C5) |
제약조건
- (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.
예
// %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_siz<es = arra>yi64: 2, 2
}< : (ten>sor4x4xi<32,> tensori<64,> te>nsori64<) - ten>sor2x2xi32
// %result: [
// [1, 1],
// [1, 1]
// ]
dynamic_update_slice
시맨틱스
start_indices에서 시작하는 슬라이스가 update의 값으로 업데이트된다는 점을 제외하고 operand 텐서와 동일한 result 텐서를 생성합니다.
더 공식적으로 result[result_index]는 다음과 같이 정의됩니다.
update[update_index](0 <= update_index < shape(update)인 경우) 여기서0 <= update_index < shape(update)는 다음과 같습니다.adjusted_start_indices = clamp(0, start_indices, shape(operand) - shape(update)).update_index = result_index - adjusted_start_indices.
- 그 밖의 경우에는
operand[result_index]입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1~C4), (C6) |
| (I2) | update |
텐서 또는 텐서별 양자화 텐서 | (C2), (C3), (C6) |
| (I3) | start_indices |
정수 유형의 0차원 텐서 가변 개수 | (C4), (C5) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (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)
예
// %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)
< : (ten>sor4x4xi<32, ten>sor2x2xi<32,> tensori<64,> te>nsori64<) - ten>sor4x4xi32
// %result: [
// [1, 1, 1, 1],
// [1, 1, 1, 1],
// [1, 1, 1, 1],
// [1, 1, 1, 1]
// ]
지수
시맨틱스
operand 텐서에 요소별 지수 연산을 수행하여 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
exp - 복소수의 경우: 복소 지수입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(exponential, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.exponential"(%operand)< : (ten>sor>2x2xf64<) - ten>sor2x2xf64
// %result: [[1.0, 2.7182818284590451], [7.3890560989306504, 20.085536923187668]]
exponential_minus_one
시맨틱스
operand 텐서에 요소별 지수 빼기 1 연산을 수행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
expm1 - 복소수의 경우 복소 지수 빼기 1입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(exponential_minus_one, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [0.0, 1.0]
%result = "stablehlo.exponential_minus_one"(%operand)< : (t>ens>or2xf64<) - t>ensor2xf64
// %result: [0.0, 1.71828187]
fft
시맨틱스
실수 및 복소수 입력/출력에 대해 순방향 및 역방향 푸리에 변환을 실행합니다.
fft_type는 다음 중 하나입니다.
FFT: 복소수에서 복소수로의 FFT를 전달합니다.IFFT: 복소수-복소수 역 FFT입니다.RFFT: 실제-복소수 FFT를 전달합니다.IRFFT: 역실수-복소수 FFT입니다 (복소수를 취하고 실수를 반환함).
더 공식적으로 복소수 유형의 1차원 텐서를 입력으로 사용하고 동일한 유형의 1차원 텐서를 출력으로 생성하며 이산 푸리에 변환을 계산하는 함수 fft가 주어졌을 때
fft_type = FFT의 경우 result는 L = size(fft_length)인 일련의 L 계산의 최종 결과로 정의됩니다. 예를 들어 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]).
또한 동일한 유형 서명이 있고 fft의 역수를 계산하는 함수 ifft가 주어졌다고 가정해 보겠습니다.
fft_type = IFFT의 경우 result는 fft_type = FFT 계산의 역으로 정의됩니다. 예를 들어 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, ..., :]).
또한 부동 소수점 유형의 1차원 텐서를 사용하고 동일한 부동 소수점 시맨틱스의 복소수 유형의 1차원 텐서를 생성하며 다음과 같이 작동하는 함수 rfft가 있다고 가정합니다.
rfft(real_operand) = truncated_result여기서complex_operand... = (real_operand..., 0.0).complex_result = fft(complex_operand).truncated_result = complex_result[:(rank(complex_result) / 2 + 1)].
(실수 피연산자에 대해 이산 푸리에 변환이 계산되면 결과의 첫 번째 N/2 + 1 요소가 나머지 결과를 명확하게 정의하므로 중복 요소를 계산하지 않도록 rfft의 결과가 잘립니다.)
fft_type = RFFT의 경우 result는 L = size(fft_length)인 일련의 L 계산의 최종 결과로 정의됩니다. 예를 들어 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]).
마지막으로 동일한 유형 서명이 있고 rfft의 역수를 계산하는 함수 irfft가 주어집니다.
fft_type = IRFFT의 경우 result는 fft_type = RFFT 계산의 역으로 정의됩니다. 예를 들어 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, ..., :]).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 | (C1), (C2), (C4), (C5) |
| (I2) | fft_type |
FFT, IFFT, RFFT, IRFFT의 enum |
(C2), (C5) |
| (I3) | fft_length |
si64 유형의 1차원 텐서 상수 |
(C1), (C3), (C4) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 | (C2), (C4), (C5) |
제약조건
- (C1)
size(fft_length) <= rank(operand). - (C2)
operand및result요소 유형 간의 관계는 다음과 같이 다양합니다.fft_type = FFT,element_type(operand),element_type(result)의 복잡한 유형이 동일한 경우fft_type = IFFT,element_type(operand),element_type(result)의 복잡한 유형이 동일한 경우fft_type = RFFT,element_type(operand)이 부동 소수점 유형이고element_type(result)이 동일한 부동 소수점 시맨틱스의 복잡한 유형인 경우fft_type = IRFFT,element_type(operand)이 복잡한 유형이고element_type(result)이 동일한 부동 소수점 시맨틱스의 부동 소수점 유형인 경우
- (C3)
1 <= size(fft_length) <= 3. - (C4)
operand및result중에 부동 소수점 유형의 텐서real가 있으면shape(real)[-size(fft_length):] = fft_length입니다. - (C5) 다음을 제외한
shape(result) = shape(operand)fft_type = RFFT인 경우dim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1입니다.fft_type = IRFFT인 경우dim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1입니다.
예
// %operand: [(1.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
%result = "stablehlo.fft"(%operand) {
fft_type = <#stablehloff>t_type FFT,
fft_leng<th = a>rrayi64: 4
}< : (tenso<r4x>>com>plexf32<) - tenso<r4x>>complexf32
// %result: [(1.0, 0.0), (1.0, 0.0), (1.0, 0.0), (1.0, 0.0)]
층
시맨틱스
operand 텐서의 요소별 바닥을 실행하고 result 텐서를 생성합니다.
IEEE-754 사양의 roundToIntegralTowardNegative 작업을 구현합니다. 양자화된 유형의 경우 dequantize_op_quantize(floor, operand, type(result))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [-0.8166, -0.2530, 0.2530, 0.8166, 2.0]
%result = "stablehlo.floor"(%operand)< : (t>ens>or5xf32<) - t>ensor5xf32
// %result: [-1.0, -1.0, 0.0, 0.0, 2.0]
gather
시맨틱스
start_indices에 지정된 오프셋에서 operand 텐서의 슬라이스를 수집하고 result 텐서를 생성합니다.
다음 다이어그램은 구체적인 예를 사용하여 result의 요소가 operand의 요소에 매핑되는 방식을 보여줍니다. 다이어그램에서는 몇 가지 result 인덱스를 예로 들어 어떤 operand 인덱스에 해당하는지 자세히 설명합니다.
더 격식 있게 표현하면 다음과 같습니다. result[result_index] = operand[operand_index]
batch_dims = [d for d in axes(result) and d not in offset_dims].batch_index = result_index[batch_dims...].start_index은 다음과 같이 정의됩니다.start_indices[bi0, ..., :, ..., biN](여기서bi은batch_index의 개별 요소이고:은index_vector_dim색인에 삽입됨)입니다(index_vector_dim<rank(start_indices)인 경우).- 그 밖의 경우에는
[start_indices[batch_index]]입니다.
axes(operand)의d_operand에 대해full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand])d_operand = start_index_map[d_start]인 경우- 그 밖의 경우에는
full_start_index[d_operand] = 0입니다.
axes(operand)의d_operand에 대해d_operand = operand_batching_dims[i_batching]및d_start = start_indices_batching_dims[i_batching]인 경우full_batching_index[d_operand] = batch_index[d_start - (d_start < index_vector_dim ? 0 : 1)]- 그 밖의 경우에는
full_batching_index[d_operand] = 0입니다.
offset_index = result_index[offset_dims...].full_offset_index = [oi0, ..., 0, ..., oiN](여기서oi은offset_index의 개별 요소이고0은collapsed_slice_dims및operand_batching_dims의 색인에 삽입됨)operand_index = full_start_index + full_batching_index + full_offset_index.
indices_are_sorted이 true이면 구현은 start_indices이 start_index_map에 관해 정렬되었다고 가정할 수 있습니다. 그렇지 않으면 동작이 정의되지 않습니다. 더 공식적으로는 indices(result), full_start_index(i1) <= full_start_index(i2)의 모든 i1 < i2에 대해
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1), (C8), (C11), (C17), (C19~C21), (C23) |
| (I2) | start_indices |
정수형 텐서 | (C2~C3), (C14), (C17), (C22) |
| (I3) | offset_dims |
si64 유형의 1차원 텐서 상수 |
(C1), (C4~C5), (C22) |
| (I4) | collapsed_slice_dims |
si64 유형의 1차원 텐서 상수 |
(C1), (C6~C9), (C22) |
| (I5) | operand_batching_dims |
si64 유형의 1차원 텐서 상수 |
(C1), (C6), (C10~C12), (C16~C18), (C22) |
| (I6) | start_indices_batching_dims |
si64 유형의 1차원 텐서 상수 |
(C13~C17) |
| (I7) | start_index_map |
si64 유형의 1차원 텐서 상수 |
(C3), (C18-C19) |
| (I8) | index_vector_dim |
si64 유형의 상수 |
(C2-C3), (C15), (C22) |
| (I9) | slice_sizes |
si64 유형의 1차원 텐서 상수 |
(C9), (C12), (C20~C22) |
| (I10) | indices_are_sorted |
i1 유형의 상수 |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C5), (C22~C23) |
제약조건
- (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)여기서:batch_dim_sizes = shape(start_indices)와 유사하지만index_vector_dim에 해당하는start_indices의 측정기준 크기는 포함되지 않습니다.offset_dim_sizes = slice_sizes에서collapsed_slice_dims및operand_batching_dims에 해당하는slice_sizes의 측정기준 크기가 포함되지 않는 경우를 제외하고는 동일합니다.combine는batch_dims에 해당하는 축에batch_dim_sizes를 배치하고offset_dims에 해당하는 축에offset_dim_sizes를 배치합니다.
- (C23)
element_type(operand) = element_type(result).
예
// %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 = #stable<hlo.gather
offset_dims = [3, 4],
collapsed_slice_dims = [1],
operand_batching_dims = [0],
start_indices_batching_dims = [1],
start_index_map = [2, 1],
index_vect>or_dim = 3,
slice_siz<es = arrayi64: >1, 1, 2, 2,
indices_are_sorted = false
}< : (tensor2>x3x4x2xi<32, tensor2>x2x>3x2xi64<) - tensor2x2>x3x2x2xi32
// %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
시맨틱스
operand의 지정된 dimension의 크기를 생성합니다. 더 공식적으로는 result = dim(operand, dimension)입니다. 시맨틱스는 유형의 모양 구성요소에만 관련됩니다. 요소 유형은 무엇이든 될 수 있습니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 양자화된 텐서 | (C1) |
| (I2) | dimension |
si64 유형의 상수 |
(C1) |
출력
| 이름 | 유형 |
|---|---|
result |
si32 유형의 0차원 텐서 |
제약조건
- (C1)
0 <= dimension < rank(operand).
예
// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.get_dimension_size"(%operand) {
dimension = 1 : i64
}< : (ten>sor>2x3xi64<) -> tensori32
// %result: 3
get_tuple_element
시맨틱스
operand 튜플의 index 위치에서 요소를 추출하고 result를 생성합니다. 더 공식적으로는 result = operand[index]입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
tuple | (C1), (C2) |
| (I2) | index |
si32 유형의 상수 |
(C1), (C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
값 무관 | (C2) |
제약조건
- (C1)
0 <= index < size(operand). - (C2)
type(result) = tuple_element_types(operand)[index]
예
// %operand: ([1.0, 2.0], (3))
%result = "stablehlo.get_tuple_element"(<%operand) {index >= 0 : i32<} : (t<uplet>ensor2x<f64, t<upl>>>ete>nsori64<) - t>ensor2xf64
// %result: [1.0, 2.0]
만약에
시맨틱스
pred 값에 따라 true_branch 또는 false_branch에서 정확히 하나의 함수를 실행한 결과를 생성합니다. 더 공식적으로는 result =
pred ? true_branch() : false_branch()입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | pred |
i1 유형의 0차원 텐서 |
|
| (I2) | true_branch |
함수 | (C1-C3) |
| (I3) | false_branch |
함수 | (C1), (C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서, 양자화된 텐서 또는 토큰 | (C3) |
제약조건
- (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).
예
// %result_true_branch: 10
// %result_false_branch: 11
// %pred: true
%result = "stablehlo.if"(%pred) ({
"stablehlo.return"(%result_tr<ue_>bra>nch) : (tensori32) - ()
}, {
"stablehlo.return"(%<res>ult>_false_branch) :< (>ten>sori32)< - >()
}) : (tensori1) - tensori32
// %result: 10
imag
시맨틱스
operand에서 요소별로 허수부를 추출하여 result 텐서를 생성합니다. 더 공식적으로 각 요소 x에 대해 imag(x) = is_complex(x) ? imaginary_part(x) :
constant(0, element_type(result))입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 | (C1), (C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 유형의 텐서 | (C1), (C2) |
제약조건
- (C1)
shape(result) = shape(operand). - (C2)
element_type(result)은 다음과 같이 정의됩니다.is_complex(operand)인 경우complex_element_type(element_type(operand))- 그 밖의 경우에는
element_type(operand)입니다.
예
// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.imag"(%operand)< : (tenso<r2x>>com>plexf32<) - t>ensor2xf32
// %result: [2.0, 4.0]
인피드
시맨틱스
피드에서 데이터를 읽고 results를 생성합니다.
infeed_config의 시맨틱은 구현으로 정의됩니다.
results은 먼저 오는 페이로드 값과 마지막에 오는 토큰으로 구성됩니다. 향후 명확성을 개선하기 위해 페이로드와 토큰을 두 개의 별도 출력으로 분할할 계획입니다(#670).
입력
| 라벨 | 이름 | 유형 |
|---|---|---|
| (I1) | token |
token |
| (I2) | infeed_config |
string 유형의 상수 |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서, 양자화된 텐서 또는 토큰 | (C1-C3) |
제약조건
- (C1)
0 < size(results). - (C2)
is_empty(result[:-1])또는is_tensor(type(results[:-1])) - (C3)
is_token(type(results[-1])).
예
// %token: !stablehlo.token
// infeed_queue[0]: [[1, 2], [3, 4]]
// infeed_queue[1]: [[5, 6], [7, 8]]
%results0:2 = "stablehlo.infeed"(%token) {
infeed_config = ""
} : >(!stable<hlo.tok>en) - (tensor2x2xi64, !stablehlo.token)
// results0#0: [[1, 2], [3, 4]]
%results1:2 = "stablehlo.infeed"(%token) {
infeed_config> = "<;">
} : (!stablehlo.token) - (tensor2x2xi64, !stablehlo.token)
// results1#0: [[5, 6], [7, 8]]
iota
시맨틱스
iota_dimension 차원을 따라 0부터 시작하여 증가하는 값으로 output 텐서를 채웁니다. 더 공식적으로 말하면
output[output_index] = constant(is_quantized(output) ?
quantize(output_index[iota_dimension], element_type(output)) :
output_index[iota_dimension], element_type(output)).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | iota_dimension |
si64 |
(C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
output |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
0 <= iota_dimension < rank(output).
예
%output = "stablehlo.iota"() {
iota_dimension = 0 : i6>4
} : (<) - ten>sor4x5xi32
// %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_dimensio>n = 1 :< i64
} >: () - tensor4x5xi32
// %output: [
// [0, 1, 2, 3, 4],
// [0, 1, 2, 3, 4],
// [0, 1, 2, 3, 4],
// [0, 1, 2, 3, 4]
// ]
is_finite
시맨틱스
x의 값이 유한한지 (즉, +Inf, -Inf, NaN이 아님) 요소별로 확인하고 y 텐서를 생성합니다. IEEE-754 사양의 isFinite 작업을 구현합니다. 양자화된 유형의 경우 결과는 항상 true입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | x |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
y |
불리언 유형의 텐서 | (C1) |
제약조건
- (C1)
shape(x) = shape(y).
예
// Logical values: -Inf, +Inf, NaN, ...
// %x: [0xFFF0000000000000, 0x7FF0000000000000, 0x7FF8000000000000, -10.0, -0.0, 0.0, 10.0]
%y = "stablehlo.is_finite"(%x)< : (tens>or7xf64<) - >tensor7xi1
// %y: [false, false, false, true, true, true, true]
로그
시맨틱스
operand 텐서에 요소별 로그 연산을 수행하여 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
log - 복소수의 경우 복소수 로그입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(log, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [[1.0, 2.0], [3.0, 4.0]]
%result = "stablehlo.log"(%operand)< : (ten>sor>2x2xf64<) - ten>sor2x2xf64
// %result: [[0.0, 0.69314718055994529], [1.0986122886681098, 1.3862943611198906]]
log_plus_one
시맨틱스
operand 텐서에 요소별 로그 더하기 1 연산을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
logp1 - 복소수의 경우:
complex(log(hypot(real(x) + 1, imag(x))), atan2(imag(x), real(x) + 1)) - 양자화된 유형의 경우:
dequantize_op_quantize(log_plus_one, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [0.0, -0.999, 7.0, 6.38905621, 15.0]
%result = "stablehlo.log_plus_one"(%operand)< : (t>ens>or5xf64<) - t>ensor5xf64
// %result: [0.0, -6.90776825, 2.07944155, 2.0, 2.77258873]
로지스틱
시맨틱스
operand 텐서에 요소별 로지스틱 연산을 수행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
division(1, addition(1, exp(-x))) - 복소수의 경우: 복소수 로지스틱
- 양자화된 유형의 경우:
dequantize_op_quantize(logistic, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.logistic"(%operand)< : (ten>sor>2x2xf64<) - ten>sor2x2xf64
// %result: [[0.5, 0.73105858], [0.88079708, 0.95257413]]
지도
시맨틱스
dimensions를 따라 inputs에 맵 함수 computation를 적용하고 result 텐서를 생성합니다.
더 공식적으로는 result[result_index] = computation(inputs...[result_index])입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | inputs |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C1~C4) |
| (I2) | dimensions |
si64 유형의 1차원 텐서 상수 |
(C3) |
| (I3) | computation |
함수 | (C4) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C1), (C4) |
제약조건
- (C1)
shape(inputs...) = shape(result). - (C2)
0 < size(inputs) = N - (C3)
dimensions = range(rank(inputs[0])). - (C4)
computation의 유형은(tensor<E0>, ..., tensor<EN-1>) -> tensor<E'>이며 여기서Ei = element_type(inputs[i])및E' = element_type(result)입니다.
예
// %input0: [[0, 1], [2, 3]]
// %input1: [[4, 5], [6, 7]]
%result = "stablehlo.map"(%input0, %input1) ({
^bb0(%ar<g0:> tensori64, %ar<g1:> tensori64):
%0 = stablehlo.multiply %arg0, %arg<1 :> tensori64
stablehlo.return %<0 :> tensori64
}) {
dimensio<ns = arra>yi64: 0, 1
}< : (ten>sor2x2xi<64, ten>sor>2x2xi64<) - ten>sor2x2xi64
// %result: [[0, 5], [12, 21]]
최대
시맨틱스
lhs 및 rhs 텐서에 요소별 최대 연산을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 불리언: 논리합
- 정수의 경우: 정수 최대값입니다.
- 부동 소수점의 경우: IEEE-754의
maximum - 복소수의 경우:
(real, imaginary)쌍의 사전식 최대값입니다. 복소수에 순서를 적용하면 놀라운 의미가 발생하므로 향후 이 작업에 대한 복소수 지원을 삭제할 계획입니다 (#560). - 양자화된 유형의 경우:
dequantize_op_quantize(maximum, lhs, rhs, type(result)).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I2) | rhs |
텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).
예
// %lhs: [[1, 2], [7, 8]]
// %rhs: [[5, 6], [3, 4]]
%result = "stablehlo.maximum"(%lhs, %rhs)< : (ten>sor2x2xi<32, ten>sor>2x2xi32<) - ten>sor2x2xi32
// %result: [[5, 6], [7, 8]]
최소
시맨틱스
lhs 및 rhs 텐서에 대해 요소별 최소 연산을 수행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 불리언: 논리곱
- 정수의 경우: 정수 최소값입니다.
- 부동 소수점의 경우: IEEE-754의
minimum - 복소수의 경우
(real, imaginary)쌍의 사전식 최소값입니다. 복소수에 순서를 적용하면 놀라운 의미가 발생하므로 향후 이 작업에 대한 복소수 지원을 삭제할 계획입니다 (#560). - 양자화된 유형의 경우:
dequantize_op_quantize(minimum, lhs, rhs, type(result)).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I2) | rhs |
텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).
예
// %lhs: [[1, 2], [7, 8]]
// %rhs: [[5, 6], [3, 4]]
%result = "stablehlo.minimum"(%lhs, %rhs)< : (ten>sor2x2xi<32, ten>sor>2x2xi32<) - ten>sor2x2xi32
// %result: [[1, 2], [3, 4]]
곱하기
시맨틱스
두 텐서 lhs와 rhs의 요소별 곱을 수행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 불리언: 논리곱
- 정수의 경우: 정수 곱셈
- 부동 소수점의 경우: IEEE-754의
multiplication - 복소수의 경우 복소수 곱셈입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(multiply, lhs, rhs, type(result)).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I2) | rhs |
텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.multiply"(%lhs, %rhs)< : (ten>sor2x2xi<32, ten>sor>2x2xi32<) - ten>sor2x2xi32
// %result: [[5, 12], [21, 32]]
negate
시맨틱스
operand 텐서의 요소별 부정을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부호 있는 정수의 경우: 정수 부정입니다.
- 부호 없는 정수의 경우: 부호 있는 정수로 bitcast, 정수 부정, 부호 없는 정수로 다시 bitcast
- 부동 소수점의 경우: IEEE-754의
negate - 복소수의 경우 복소수 부정입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(negate, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// Negation operation with integer Tensors
// %operand: [0, -2]
%result = "stablehlo.negate"(%operand)< : (t>ens>or2xi32<) - t>ensor2xi32
// %result: [0, 2]
// Negation operation with with complex tensors
// %operand: (2.5, 0.0)
%result = "stablehlo.negate"<(%operand<) :>> (t>ensor1x<complexf3<2) >>- tensor1xcomplexf32
// %result: [-2.5, -0.0]
소속되어 있지 않은
시맨틱스
텐서 operand의 요소별 NOT을 실행하고 result 텐서를 생성합니다.
요소 유형에 따라 다음을 실행합니다.
- 불리언: 논리 부정
- 정수의 경우 비트 NOT입니다.
인수
| 이름 | 유형 | 제약조건 |
|---|---|---|
operand |
불리언 또는 정수 유형의 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
불리언 또는 정수 유형의 텐서 | (C1) |
제약조건
- (C1)
type(operand) = type(result).
예
// Bitwise operation with with integer tensors
// %operand: [[1, 2], [3, 4]]
%result = "stablehlo.not"(%operand)< : (ten>sor>2x2xi32<) - ten>sor2x2xi32
// %result: [[-2, -3], [-4, -5]]
// Bitwise operation with with boolean tensors
// %operand: [true, false]
%result = "stablehlo.not"<(%op>era>nd) : (<tens>or2xi1) - tensor2xi1
// %result: [false, true]
optimization_barrier
시맨틱스
operand를 생성하는 작업이 result에 종속된 작업보다 먼저 실행되도록 하고 컴파일러 변환이 장벽을 넘어 작업을 이동하지 못하도록 합니다. 그 외에는 작업이 ID, 즉 result = operand입니다.
인수
| 이름 | 유형 | 제약조건 |
|---|---|---|
operand |
가변 개수의 텐서, 텐서별 양자화 텐서 또는 토큰 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
가변 개수의 텐서, 텐서별 양자화 텐서 또는 토큰 | (C1) |
제약조건
- (C1)
type(operand...) = type(result...).
예
// %operand0: 0.0
// %operand1: 1.0
%result0, %result1 = "stablehlo.optimization_barrier"(%operand0, %operand1)< : >(tensorf<32,> te>nsorf32)< - >(tensorf<32,> tensorf32)
// %result0: 0.0
// %result1: 1.0
또는
시맨틱스
두 텐서 lhs와 rhs의 요소별 OR을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 불리언: 논리합
- 정수의 경우 비트 OR입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
정수 또는 불리언 유형의 텐서 | (C1) |
| (I2) | rhs |
정수 또는 불리언 유형의 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수 또는 불리언 유형의 텐서 | (C1) |
제약조건
- (C1)
type(lhs) = type(rhs) = type(result).
예
// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.or"(%lhs, %rhs)< : (ten>sor2x2xi<32, ten>sor>2x2xi32<) - ten>sor2x2xi32
// %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>2x2>xi1, te<nsor2x>2xi1) - tensor2x2xi1
// %result: [[false, true], [true, true]]
아웃피드
시맨틱스
inputs을 아웃피드에 쓰고 result 토큰을 생성합니다.
outfeed_config의 시맨틱은 구현으로 정의됩니다.
입력
| 라벨 | 이름 | 유형 |
|---|---|---|
| (I1) | inputs |
가변 개수의 텐서 또는 양자화된 텐서 |
| (I2) | token |
token |
| (I3) | outfeed_config |
string 유형의 상수 |
출력
| 이름 | 유형 |
|---|---|
result |
token |
예
%result = "stablehlo.outfeed"(%input0, %token) {
outfeed_config = &quo<t;"<>/span>
} : (tensor2x2x2xi64,> !stablehlo.token) - !stablehlo.token
패드
시맨틱스
주어진 padding_value을 사용하여 텐서 주변과 텐서의 요소 사이에 패딩을 추가하여 operand을 확장합니다.
edge_padding_low 및 edge_padding_high는 각 차원의 하위 (색인 0 옆) 및 상위 (최고 색인 옆)에 추가된 패딩의 양을 각각 지정합니다. 패딩 양수는 음수일 수 있으며, 음수 패딩의 절대값은 지정된 차원에서 삭제할 요소 수를 나타냅니다.
interior_padding는 각 차원에서 두 요소 사이에 추가된 패딩의 양을 지정하며 음수일 수 없습니다. 내부 패딩은 가장자리 패딩 전에 발생하므로 음수 가장자리 패딩은 내부 패딩 피연산자에서 요소를 삭제합니다.
더 공식적으로 result[result_index]은 다음과 같이 정의됩니다.
result_index = edge_padding_low + operand_index * (interior_padding + 1)인 경우operand[operand_index]- 그 밖의 경우에는
padding_value입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1), (C2), (C4) |
| (I2) | padding_value |
0차원 텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I3) | edge_padding_low |
si64 유형의 1차원 텐서 상수 |
(C1), (C4) |
| (I4) | edge_padding_high |
si64 유형의 1차원 텐서 상수 |
(C1), (C4) |
| (I5) | interior_padding |
si64 유형의 1차원 텐서 상수 |
(C2-C4) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C3~C6) |
제약조건
- (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
예
// %operand: [
// [1, 2, 3],
// [4, 5, 6]
// ]
// %padding_value: 0
%result = "stablehlo.pad"(%operand, %padding_value) {
edge_padding_l<ow = arra>yi64: 0, 1,
edge_padding_hi<gh = arra>yi64: 2, 1,
interior_paddi<ng = arra>yi64: 1, 2
}< : (ten>sor2x3xi<32,> te>nsori32<) - ten>sor5x9xi32
// %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
시맨틱스
현재 프로세스의 partition_id을 생성합니다.
출력
| 이름 | 유형 |
|---|---|
result |
ui32 유형의 0차원 텐서 |
예
%result = "stablehlo.partition_id">;() : (<) - >tensorui32
popcnt
시맨틱스
operand 텐서에 설정된 비트 수를 요소별로 계산하고 result 텐서를 생성합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
정수형 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수형 텐서 | (C1) |
제약조건
- (C1)
type(operand) = type(result).
예
// %operand: [0, 1, 2, 127]
%result = "stablehlo.popcnt"(%operand)< : (t>ens>or4xi64<) - t>ensor4xi64
// %result: [0, 1, 1, 7]
전력
시맨틱스
rhs 텐서로 lhs 텐서의 요소별 지수화를 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 정수의 경우: 정수 거듭제곱입니다.
- 부동 소수점의 경우: IEEE-754의
pow - 복소수의 경우 복소수 지수입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(power, lhs, rhs, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I2) | rhs |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %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)< : (t>ensor6xf<64, t>ens>or6xf64<) - t>ensor6xf64
// %result: [4.0, 0.0, -nan, 25.0, 0.333333343, inf]
real
시맨틱스
operand에서 요소별로 실제 부분을 추출하고 result 텐서를 생성합니다. 더 공식적으로 각 요소 x에 대해 real(x) = is_complex(x) ? real_part(x) : x입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 | (C1), (C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 유형의 텐서 | (C1), (C2) |
제약조건
- (C1)
shape(result) = shape(operand). - (C2)
element_type(result)은 다음과 같이 정의됩니다.is_complex(operand)인 경우complex_element_type(element_type(operand))- 그 밖의 경우에는
element_type(operand)입니다.
예
// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.real"(%operand)< : (tenso<r2x>>com>plexf32<) - t>ensor2xf32
// %result: [1.0, 3.0]
recv
시맨틱스
channel_id로 채널에서 데이터를 수신하고 results를 생성합니다.
is_host_transfer이 true이면 작업이 호스트에서 데이터를 전송합니다. 그렇지 않으면 source_target_pairs 값을 기반으로 다른 기기에서 데이터를 전송합니다. 이 플래그는 channel_type에 제공된 정보를 중복하므로 향후에는 둘 중 하나만 유지할 계획입니다(#666). is_host_transfer = false이고 source_target_pairs가 None이거나 비어 있으면 정의되지 않은 동작으로 간주됩니다.
results은 먼저 오는 페이로드 값과 마지막에 오는 토큰으로 구성됩니다. 향후 명확성을 개선하기 위해 페이로드와 토큰을 두 개의 별도 출력으로 분할할 계획입니다(#670).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | token |
token |
|
| (I2) | channel_id |
si64 유형의 상수 |
|
| (I3) | channel_type |
DEVICE_TO_DEVICE 및 DEVICE_TO_HOST의 열거형 |
(C5) |
| (I4) | is_host_transfer |
i1 유형의 상수 |
(C5~C6) |
| (I5) | source_target_pairs |
si64 유형의 2차원 텐서 상수 |
(C1~C4), (C6) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서, 양자화된 텐서 또는 토큰 | (C2-C4) |
제약조건
- (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, 여기서N은 다음과 같이 정의됩니다.cross_replica이 사용된 경우num_replicascross_partition이 사용된 경우num_partitions
- (C5)
channel_type은 다음과 같이 정의됩니다.is_host_transfer = true인 경우DEVICE_TO_HOST- 그 밖의 경우에는
DEVICE_TO_DEVICE입니다.
예
%results0, %results1 = "stablehlo.recv"(%token) {
channel_handle = #stablehlo.chan<nel_handlehandle = 0>, type = 1,
is_host_transfer = false,
source_target_pai<rs = dense[[0, 1>], [1, 2]<] : ten>sor2x2xi64
} : (!stablehl>o.token)< - (ten>sor2x2xi64, !stablehlo.token)
reduce
시맨틱스
dimensions를 따라 inputs 및 init_values에 축소 함수 body를 적용하고 results 텐서를 생성합니다.
축소 순서는 구현에 따라 정의됩니다. 즉, 모든 구현에서 모든 입력에 대해 작업이 동일한 결과를 생성하도록 보장하려면 body와 init_values가 모노이드를 형성해야 합니다. 하지만 이 조건은 인기 있는 많은 감소에 적용되지 않습니다. 예를 들어 body의 부동 소수점 덧셈과 init_values의 0은 실제로 모노이드를 형성하지 않습니다. 부동 소수점 덧셈은 결합 법칙이 아니기 때문입니다.
더 공식적으로는 results...[j0, ..., jR-1] = reduce(input_slices_converted)입니다. 여기서
input_slices = inputs...[j0, ..., :, ..., jR-1]: 여기서:은dimensions에 삽입됩니다.input_slices_converted = to_destination_type(input_slices..., type(func_inputs(body)[:len(func_inputs(body))//2])...).init_values_converted = to_destination_type(init_values..., type(func_inputs(body)[len(func_inputs(body))//2:])...).reduce(input_slices_converted) = exec(schedule)(일부 이진 트리schedule의 경우) 여기서exec(node) = body(exec(node.left), exec(node.right)).exec(leaf) = leaf.value.
schedule는 구현 정의 전체 바이너리 트리로, 중위 순회는 다음으로 구성됩니다.index의 사전 오름차순으로index_space(input_slices_converted)에 있는 모든index의input_slices_converted...[index]값- 구현 정의 위치에 구현 정의된 양의
init_values_converted가 삽입됩니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | inputs |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C1~C4), (C6), (C7) |
| (I2) | init_values |
0차원 텐서 또는 텐서별 양자화 텐서의 가변 개수 | (C2), (C3) |
| (I3) | dimensions |
si64 유형의 1차원 텐서 상수 |
(C4), (C5), (C7) |
| (I4) | body |
함수 | (C6) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C3), (C7), (C8) |
제약조건
- (C1)
same(shape(inputs...)). - (C2)
element_type(inputs...) = element_type(init_values...) - (C3)
0 < size(inputs) = size(init_values) = size(results) = N. - (C4)
0 <= dimensions < rank(inputs[0]) - (C5)
is_unique(dimensions). - (C6)
body의 유형은(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)이며 여기서is_promotable(element_type(inputs[i]), Ei)입니다. - (C7)
shape(results...) = shape(inputs...)(dimensions에 해당하는inputs...의 측정기준 크기는 제외) - (C8)
[0,N)의 모든i에 대해element_type(results[i]) = Ei
예
// %input = [[0, 1, 2, 3, 4, 5]]
// %init_value = 0
%result = "stablehlo.reduce"(%input, %init_value) ({
^bb0(%ar<g0:> tensori64, %ar<g1:> tensori64):
%0 = "stablehlo.add"(%ar<g0,> %arg1) <: (>ten>sori64,< te>nsori64) - tensori64
"stable<hlo>.re>turn"(%0) : (tensori64) <- ()
}>) {
dimens<ions = >arrayi64<: 1>
} >: (tens<or1x6>xi64, tensori64) - tensor1xi64
// %result = [15]
reduce_precision
시맨틱스
operand를 exponent_bits 및 mantissa_bits을 사용하는 다른 부동 소수점 유형으로 변환한 후 원래 부동 소수점 유형으로 다시 변환하여 output 텐서를 생성합니다.
더 공식적으로 표현하면 다음과 같습니다.
- 원래 값의 가수 비트는
roundToIntegralTiesToEven의미 체계를 사용하여mantissa_bits로 표현할 수 있는 가장 가까운 값으로 원래 값을 반올림하도록 업데이트됩니다. - 그런 다음
mantissa_bits이 원래 값의 가수 비트 수보다 작으면 가수 비트가mantissa_bits로 잘립니다. - 그런 다음 중간 결과의 지수 비트가
exponent_bits에서 제공하는 범위에 맞지 않으면 중간 결과가 원래 부호를 사용하여 무한대로 오버플로되거나 원래 부호를 사용하여 0으로 언더플로됩니다. - 양자화된 유형의 경우
dequantize_op_quantize( lambda operand: reduce_precision(operand, exponent_bits, mantissa_bits), operand, type(result))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I2) | exponent_bits |
si32 유형의 상수 |
(C2) |
| (I3) | mantissa_bits |
si32 유형의 상수 |
(C3) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
output |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(output). - (C2)
1 <= exponent_bits - (C3)
0 <= mantissa_bits.
예
// 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
}< : (t>ens>or6xf64<) - t>ensor6xf64
// Logical values: +Inf, NaN, 0.0, 0.0, 65504.0, +Inf
// %output: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0.0, 0.0, 65504.0, 0x7FF0000000000000]
reduce_scatter
시맨틱스
StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 각 프로세스의 operand 텐서 값에 대해 computations을 사용하여 감소를 실행하고, scatter_dimension를 따라 감소 결과를 부분으로 분할하고, 분할된 부분을 프로세스 간에 분산하여 result를 생성합니다.
이 작업은 StableHLO 프로세스 그리드를 다음과 같이 정의된 process_groups로 분할합니다.
cross_replica(replica_groups)channel_id <= 0 and use_global_device_ids = false인 경우cross_replica_and_partition(replica_groups)channel_id > 0 and use_global_device_ids = false인 경우flattened_ids(replica_groups)channel_id > 0 and use_global_device_ids = true인 경우
그런 다음 각 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).process_group의 모든sender에 대해result@receiver = parts@sender[receiver_index]입니다. 여기서receiver_index = process_group.index(receiver)입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1), (C2), (C7), (C8) |
| (I2) | scatter_dimension |
si64 유형의 상수 |
(C1), (C2), (C8) |
| (I3) | replica_groups |
si64 유형의 2차원 텐서 상수 |
(C3~C5) |
| (I4) | channel_id |
si64 유형의 상수 |
(C6) |
| (I5) | use_global_device_ids |
i1 유형의 상수 |
(C6) |
| (I6) | computation |
함수 | (C7) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C8~C9) |
제약조건
- (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)은 다음과 같이 정의됩니다.cross_replica이 사용된 경우num_replicascross_replica_and_partition이 사용된 경우num_replicasflattened_ids이 사용된 경우num_processes
- (C5)
0 <= replica_groups < size(replica_groups). - (C6)
use_global_device_ids = true시channel_id > 0 - (C7)
computation의 유형은(tensor<E>, tensor<E>) -> (tensor<E>)이며 여기서is_promotable(element_type(operand), E)입니다. - (C8)
shape(result) = shape(operand)(단, 다음은 제외)dim(result, scatter_dimension) = dim(operand, scatter_dimension) / dim(process_groups, 1).
- (C9)
element_type(result) = E.
예
// 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(%ar<g0:> tensori64, %ar<g1:> tensori64):
%0 = "stablehlo.add"(%ar<g0,> %arg1) <: (>ten>sori64,< te>nsori64) - tensori64
"stable<hlo>.re>turn"(%0) : (tensori64) - ()
}) {
scatter_dimension = 1 :< i64,
>replica_g<roups => dense[[0, 1]] : tensor1x2xi64,
channel_hand<le = #stablehlo.chan>nel_handleha<ndle = >0, >type = <0
} : (>tensor2x4xi64) - tensor2x2xi64
//
// %result@(0, 0): [[10, 12],
// [18, 20]]
// %result@(1, 0): [[14, 16],
// [22, 24]]
reduce_window
시맨틱스
inputs 및 init_values의 윈도우에 감소 함수 body를 적용하고 results를 생성합니다.
다음 다이어그램은 구체적인 예를 사용하여 results...의 요소가 inputs...에서 계산되는 방식을 보여줍니다.
더 공식적으로는 다음과 같습니다.
results...[result_index] = reduce(windows, init_values, axes(inputs...), body)
(reduce 참고)
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).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | inputs |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C1~C4), (C6), (C8), (C10), (C12), (C13), (C15) |
| (I2) | init_values |
0차원 텐서 또는 텐서별 양자화 텐서의 가변 개수 | (C1), (C13) |
| (I3) | window_dimensions |
si64 유형의 1차원 텐서 상수 |
(C4), (C5), (C15) |
| (I4) | window_strides |
si64 유형의 1차원 텐서 상수 |
(C6), (C7), (C15) |
| (I5) | base_dilations |
si64 유형의 1차원 텐서 상수 |
(C8), (C9), (C15) |
| (I6) | window_dilations |
si64 유형의 1차원 텐서 상수 |
(C10), (C11), (C15) |
| (I7) | padding |
si64 유형의 2차원 텐서 상수 |
(C12), (C15) |
| (I8) | body |
함수 | (C13) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C1), (C14~C16) |
제약조건
- (C1)
0 < size(inputs) = size(init_values) = size(results) = N. - (C2)
same(shape(inputs...)) - (C3)
element_type(inputs...) = element_type(init_values...). - (C4)
size(window_dimensions) = rank(inputs[0]) - (C5)
0 < window_dimensions. - (C6)
size(window_strides) = rank(inputs[0]) - (C7)
0 < window_strides. - (C8)
size(base_dilations) = rank(inputs[0]) - (C9)
0 < base_dilations. - (C10)
size(window_dilations) = rank(inputs[0]). - (C11)
0 < window_dilations. - (C12)
shape(padding) = [rank(inputs[0]), 2]. - (C13)
body의 유형은(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)이며 여기서is_promotable(element_type(inputs[i]), Ei)입니다. - (C14)
same(shape(results...)) - (C15)
shape(results[0]) = num_windows각 항목의 의미는 다음과 같습니다.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)
[0,N)의 모든i에 대해element_type(results[i]) = Ei
예
// %input = [[1, 2], [3, 4], [5, 6]]
// %init_value = 0
%result = "stablehlo.reduce_window"(%input, %init_value) ({
^bb0(%ar<g0:> tensori64, %ar<g1:> tensori64):
%0 = "stablehlo.add"(%ar<g0,> %arg1) <: (>ten>sori64,< te>nsori64) - tensori64
"stable<hlo>.re>turn"(%0) : (tensori64) - ()
})< {
wind>ow_dimensions = arrayi64: <2, 1,
w>indow_strides = arrayi64: <4, 1,
b>ase_dilations = arrayi64: 2,< 1,
win>dow_dilations = arr<ayi64: 3, 1,
p>adding = <dense[[>2, 1], [0, 0<]] : te>nsor2x2x<i64>
} >: (tens<or3x2xi>64, tensori64) - tensor2x2xi64
// %result = [[0, 0], [3, 4]]
나머지
시맨틱스
피제수 lhs와 제수 rhs 텐서의 요소별 나머지를 수행하고 result 텐서를 생성합니다.
좀 더 공식적으로 말하면 결과의 부호는 피제수에서 가져오고 결과의 절대값은 항상 제수의 절대값보다 작습니다.
나머지는 lhs - d * rhs로 계산되며, 여기서 d는 다음과 같습니다.
- 정수의 경우:
stablehlo.divide(lhs, rhs) - 부동 소수점의 경우: 반올림 속성
roundTowardZero이 있는 IEEE-754의division(lhs, rhs) - 복소수의 경우: 미정(#997)
- 양자화된 유형의 경우:
dequantize_op_quantize(remainder, lhs, rhs, type(result)).
부동 소수점 요소 유형의 경우 이 작업은 d이 lhs/rhs의 정확한 값에 가장 가까운 정수 값이며 동률은 짝수로 처리되는 IEEE-754 사양의 remainder 작업과 대조됩니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I2) | rhs |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %lhs: [17, -17, 17, -17]
// %rhs: [3, 3, -3, -3]
%result = "stablehlo.remainder"(%lhs, %rhs)< : (t>ensor4xi<64, t>ens>or4xi64<) - t>ensor4xi64
// %result: [2, -2, 2, -2]
replica_id
시맨틱스
현재 프로세스의 replica_id을 생성합니다.
출력
| 이름 | 유형 |
|---|---|
result |
ui32 유형의 0차원 텐서 |
예
%result = "stablehlo.replica_id">;() : (<) - >tensorui32
reshape
시맨틱스
operand 텐서를 result 텐서로 재구성합니다. 개념적으로는 동일한 표준 표현을 유지하되 모양을 tensor<2x3xf32>에서 tensor<3x2xf32> 또는 tensor<6xf32>로 변경하는 것과 같습니다.
더 공식적으로는 result[result_index] = operand[operand_index]입니다. 여기서 result_index과 operand_index은 index_space(result) 및 index_space(operand)의 사전순 정렬에서 동일한 위치를 갖습니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 양자화된 텐서 | (C1-C3) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 양자화된 텐서 | (C1-C3) |
제약조건
- (C1)
element_type(result)은 다음 식으로 주어집니다.!is_per_axis_quantized(operand)인 경우element_type(operand)element_type(operand)와 동일합니다. 단,quantization_dimension(operand)및quantization_dimension(result)는 다를 수 있습니다.
- (C2)
size(operand) = size(result) - (C3)
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).
예
// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.reshape"(%operand)< : (ten>sor>2x3xi32<) - ten>sor3x2xi32
// %result: [[1, 2], [3, 4], [5, 6]]
reverse
시맨틱스
지정된 dimensions를 따라 operand의 요소 순서를 반대로 하고 result 텐서를 생성합니다. 더 공식적으로는 다음과 같습니다.
result[result_index] = operand[operand_index]
dimensions에d이 있는 경우operand_index[d] = dim(result, d) - result_index[d] - 1- 그 밖의 경우에는
operand_index[d] = result_index[d]입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1), (C3) |
| (I2) | dimensions |
si64 유형의 1차원 텐서 상수 |
(C2), (C3) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C1), (C3) |
제약조건
- (C1)
type(operand) = type(result). - (C2)
is_unique(dimensions) - (C3)
0 <= dimensions < rank(result).
예
// %operand = [[1, 2], [3, 4], [5, 6]]
%result = "stablehlo.reverse"(%operand) {
dimensio<ns = a>rrayi64: 1
}< : (ten>sor>3x2xi32<) - ten>sor3x2xi32
// %result: [[2, 1], [4, 3], [6, 5]]
rng
시맨틱스
rng_distribution 알고리즘을 사용하여 난수를 생성하고 지정된 모양 shape의 result 텐서를 생성합니다.
rng_distribution = UNIFORM인 경우 임의의 숫자는 [a, b) 간격에 걸쳐 균등 분포를 따라 생성됩니다. a >= b인 경우 동작이 정의되지 않습니다.
rng_distribution = NORMAL이면 평균 = a, 표준 편차 = b인 정규 분포에 따라 난수가 생성됩니다.
b < 0인 경우 동작이 정의되지 않습니다.
난수가 생성되는 정확한 방식은 구현에 따라 다릅니다. 예를 들어 결정적일 수도 있고 아닐 수도 있으며 숨겨진 상태를 사용할 수도 있고 아닐 수도 있습니다.
많은 이해관계자와의 대화에서 이 작업이 사실상 지원 중단된 것으로 언급되었으므로 향후 이 작업을 삭제할 계획입니다(#597).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | a |
정수, 불리언 또는 부동 소수점 유형의 0차원 텐서 | (C1), (C2) |
| (I2) | b |
정수, 불리언 또는 부동 소수점 유형의 0차원 텐서 | (C1), (C2) |
| (I3) | shape |
si64 유형의 1차원 텐서 상수 |
(C3) |
| (I4) | rng_distribution |
UNIFORM 및 NORMAL의 열거형 |
(C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수, 불리언 또는 부동 소수점 유형의 텐서 | (C1-C3) |
제약조건
- (C1)
element_type(a) = element_type(b) = element_type(result). - (C2)
rng_distribution = NORMAL시is_float(a) - (C3)
shape(result) = shape.
예
// %a = 0
// %b = 2
// %shape = [3, 3]
%result = "stablehlo.rng"(%a, %b, %shape) {
rng_distribution = <#stablehlorng_distributi>on UNIFORM
}< : >(tensori<32,> tensori<32, t>ens>or2xi64<) - ten>sor3x3xi32
// %result: [
// [1, 0, 1],
// [1, 1, 1],
// [0, 0, 0]
// ]
rng_bit_generator
시맨틱스
초기 상태 initial_state가 주어지면 의사 난수 생성기 알고리즘 rng_algorithm를 사용하여 균일한 무작위 비트로 채워진 output와 업데이트된 출력 상태 output_state를 반환합니다. 출력은 initial_state의 결정론적 함수로 보장되지만 구현 간에 결정론적이라고 보장되지는 않습니다.
rng_algorithm는 다음 중 하나입니다.
DEFAULT: 구현 정의 알고리즘입니다.THREE_FRY: Threefry 알고리즘의 구현 정의 변형입니다.*PHILOX: Philox 알고리즘의 구현 정의 변형입니다.*
* 참고: Salmon et al. SC 2011. 병렬 난수: 1, 2, 3만큼 쉽습니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | rng_algorithm |
DEFAULT, THREE_FRY, PHILOX의 enum |
(C2) |
| (I2) | initial_state |
ui64 유형의 1차원 텐서 |
(C1), (C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
output_state |
ui64 유형의 1차원 텐서 |
(C1) |
output |
정수 또는 부동 소수점 유형의 텐서 |
제약조건
- (C1)
type(initial_state) = type(output_state). - (C2)
size(initial_state)는 다음과 같이 정의됩니다.rng_algorithm = DEFAULT인 경우 구현 정의rng_algorithm = THREE_FRY인 경우2rng_algorithm = PHILOX인 경우2또는3
예
// %initial_state: [1, 2]
%output_state, %output = "stablehlo.rng_bit_generator"(%initial_state) {
rng_algorithm = <#stablehlorng_algorithm> THREE_FRY
}< : (te>nso>r2xui64)< - (te>nsor2xui<64, tens>or2x2xui64)
// %output_state: [1, 6]
// %output: [
// [9236835810183407956, 16087790271692313299],
// [18212823393184779219, 2658481902456610144]
// ]
round_nearest_afz
시맨틱스
operand 텐서에서 0에서 멀어지는 방향으로 가장 가까운 정수로 요소별 반올림을 실행하고 result 텐서를 생성합니다. IEEE-754 사양의 roundToIntegralTiesToAway 작업을 구현합니다. 양자화된 유형의 경우 dequantize_op_quantize(round_nearest_afz, operand, type(result))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand = [-2.5, 0.4, 0.5, 0.6, 2.5]
%result = "stablehlo.round_nearest_afz"(%operand)< : (t>ens>or5xf64<) - t>ensor5xf64
// %result: [-3.0, 0.0, 1.0, 1.0, 3.0]
round_nearest_even
시맨틱스
operand 텐서에서 가장 가까운 정수로 요소별 반올림을 실행하고 동점은 짝수 정수 쪽으로 처리하여 result 텐서를 생성합니다. IEEE-754 사양의 roundToIntegralTiesToEven 작업을 구현합니다. 양자화된 유형의 경우 dequantize_op_quantize(round_nearest_even, operand, type(result))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand = [-2.5, 0.4, 0.5, 0.6, 2.5]
%result = "stablehlo.round_nearest_even"(%operand)< : (t>ens>or5xf64<) - t>ensor5xf64
// %result: [-2.0, 0.0, 0.0, 1.0, 2.0]
rsqrt
시맨틱스
operand 텐서에 대해 요소별 역수 제곱근 연산을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
rSqrt - 복소수의 경우: 복소수 역수 제곱근입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(rsqrt, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [[1.0, 4.0], [9.0, 25.0]]
%result = "stablehlo.rsqrt"(%operand)< : (ten>sor>2x2xf32<) - ten>sor2x2xf32
// %result: [[1.0, 0.5], [0.33333343, 0.2]]
분산
시맨틱스
scatter_indices로 지정된 여러 슬라이스가 update_computation를 사용하여 updates 값으로 업데이트된다는 점을 제외하고 inputs 텐서와 동일한 results 텐서를 생성합니다.
다음 다이어그램은 구체적인 예를 사용하여 updates...의 요소가 results...의 요소에 매핑되는 방식을 보여줍니다. 다이어그램에서는 몇 가지 updates... 색인을 예로 선택하고 이러한 색인이 어떤 results... 색인에 해당하는지 자세히 설명합니다.
좀 더 공식적으로 index_space(updates[0])의 모든 update_index에 대해 다음이 적용됩니다.
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은 다음과 같이 정의됩니다.scatter_indices[si0, ..., :, ..., siN](여기서si는update_scatter_index의 개별 요소이고:는index_vector_dim색인에 삽입됨)입니다(index_vector_dim<rank(scatter_indices)인 경우).- 그 밖의 경우에는
[scatter_indices[update_scatter_index]]입니다.
axes(inputs[0])의d_input에 대해d_input = scatter_dims_to_operand_dims[d_start]인 경우full_start_index[d_input] = start_index[d_start]- 그 밖의 경우에는
full_start_index[d_input] = 0입니다.
axes(inputs[0])의d_input에 대해d_input = input_batching_dims[i_batching]및d_start = scatter_indices_batching_dims[i_batching]인 경우full_batching_index[d_input] = update_scatter_index[d_start - (d_start < index_vector_dim ? 0 : 1)]- 그 밖의 경우에는
full_batching_index[d_input] = 0입니다.
update_window_index = update_index[update_window_dims...].full_window_index = [wi0, ..., 0, ..., wiN](여기서wi은update_window_index의 개별 요소이고0은inserted_window_dims및input_batching_dims의 색인에 삽입됨)result_index = full_start_index + full_batching_index + full_window_index.
results = exec(schedule, inputs)(여기서 각 항목의 의미는 다음과 같습니다.)
schedule은index_space(updates[0])의 구현 정의 순열입니다.exec([update_index, ...], results) = exec([...], updated_results)각 항목의 의미는 다음과 같습니다.result_index이shape(results...)의 범위 내에 있는 경우updates_converted = to_destination_type( updates...[update_index], type(func_inputs(update_computation) [len(func_inputs(update_computation))//2:])... )updated_values = update_computation(results...[result_index], updates_converted)updated_results은results...[result_index]이updated_values...로 설정된results의 사본입니다.- 그렇지 않은 경우 다음 단계를 따릅니다.
updated_results = results.
exec([], results) = results.
indices_are_sorted이 true이면 구현은 scatter_indices이 scatter_dims_to_operand_dims에 관해 정렬되었다고 가정할 수 있습니다. 그렇지 않으면 동작이 정의되지 않습니다. 더 공식적으로는 indices(result)에서 i1 < i2에 대해 full_start_index(i1) <= full_start_index(i2)입니다.
unique_indices가 true인 경우 구현에서는 흩어져 있는 모든 result_index 색인이 고유하다고 가정할 수 있습니다. unique_indices이 true이지만 분산되는 색인이 고유하지 않으면 동작이 정의되지 않습니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | inputs |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C1), (C2), (C4~C6), (C11), (C13), (C18), (C21), (C23~C24) |
| (I2) | scatter_indices |
정수형 텐서 | (C4), (C15), (C19), (C22) |
| (I3) | updates |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C3~C6), (C8) |
| (I4) | update_window_dims |
si64 유형의 1차원 텐서 상수 |
(C2), (C4), (C7-C8) |
| (I5) | inserted_window_dims |
si64 유형의 1차원 텐서 상수 |
(C2), (C4), (C9-C11) |
| (I6) | input_batching_dims |
si64 유형의 1차원 텐서 상수 |
(C2), (C4), (C9), (C12-13), (C17-18), (C20) |
| (I7) | scatter_indices_batching_dims |
si64 유형의 1차원 텐서 상수 |
(C14-C18) |
| (I8) | scatter_dims_to_operand_dims |
si64 유형의 1차원 텐서 상수 |
(C19~C21) |
| (I9) | index_vector_dim |
si64 유형의 상수 |
(C4), (C16), (C19), (C22) |
| (I10) | indices_are_sorted |
i1 유형의 상수 |
|
| (I11) | unique_indices |
i1 유형의 상수 |
|
| (I12) | update_computation |
함수 | (C23) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C24-C25) |
제약조건
- (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)(여기서index_vector_dim에 해당하는scatter_indices의 차원 크기가 포함되지 않는다는 점을 제외하면update_scatter_dim_sizes = shape(scatter_indices)와 같습니다.update_window_dim_sizes <= shape(inputs[0])에서inserted_window_dims및input_batching_dims에 해당하는inputs[0]의 측정기준 크기가 포함되지 않습니다.combine은update_scatter_dims에 해당하는 축에update_scatter_dim_sizes을 배치하고update_window_dims에 해당하는 축에update_window_dim_sizes을 배치합니다.
- (C5)
0 < size(inputs) = size(updates) = N. - (C6)
element_type(updates...) = element_type(inputs...) - (C7)
is_unique(update_window_dims) and is_sorted(update_window_dims). - (C8)
0 <= update_window_dims < rank(updates[0]) - (C9)
is_unique(concatenate(inserted_window_dims, input_batching_dims)) - (C10)
is_sorted(inserted_window_dims). - (C11)
0 <= inserted_window_dims < rank(inputs[0]). - (C12)
is_sorted(input_batching_dims). - (C13)
0 <= input_batching_dims < rank(inputs[0])). - (C14)
is_unique(scatter_indices_batching_dims) - (C15)
0 <= scatter_indices_batching_dims < rank(scatter_indices) - (C16)
index_vector_dim not in scatter_indices_batching_dims. - (C17)
size(input_batching_dims) == size(scatter_indices_batching_dims). - (C18)
dim(inputs[0], input_batching_dims...) = dim(scatter_indices, scatter_indices_batching_dims...). - (C19)
size(scatter_dims_to_operand_dims) = index_vector_dim < rank(scatter_indices) ? dim(scatter_indices, index_vector_dim) : 1. - (C20)
is_unique(concatenate(scatter_dims_to_operand_dims, input_batching_dims)). - (C21)
0 <= scatter_dims_to_operand_dims < rank(inputs[0]). - (C22)
0 <= index_vector_dim <= rank(scatter_indices) - (C23)
update_computation의 유형은(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)이며, 여기서is_promotable(element_type(inputs[i]), Ei)입니다. - (C24)
shape(inputs...) = shape(results...). - (C25)
[0,N)의 모든i에 대해element_type(results[i]) = Ei
예
// %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(%ar<g0:> tensori64, %ar<g1:> tensori64):
%0 = "stablehlo.add"(%ar<g0,> %arg1) <: (>ten>sori64,< te>nsori64) - tensori64
"stable<hlo>.re>turn"(%0) : (tensori64) - ()
}) {
scatter_dimensio<n_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,
uniq<ue_indices >= false
<} : (tensor>2x3x4x2x<i64, tensor2x>2x3>x2xi64,< tensor2x2x>3x2x2xi64) - tensor2x3x4x2xi64
// %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]]
// ]
// ]
select
시맨틱스
각 요소가 pred의 해당 요소 값에 따라 on_true 또는 on_false 텐서에서 선택되는 result 텐서를 생성합니다.
더 공식적으로 말하면 result[result_index] = pred_element ? on_true[result_index] :
on_false[result_index]입니다. 여기서 pred_element = rank(pred) = 0 ? pred[] :
pred[result_index]입니다. 양자화된 유형의 경우 dequantize_select_quantize(pred, on_true, on_false, type(result))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | pred |
i1 유형의 텐서 |
(C1) |
| (I2) | on_true |
텐서 또는 텐서별 양자화 텐서 | (C1-C2) |
| (I3) | on_false |
텐서 또는 텐서별 양자화 텐서 | (C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C2) |
제약조건
- (C1)
rank(pred) = 0 or shape(pred) = shape(on_true). - (C2)
baseline_type(on_true) = baseline_type(on_false) = baseline_type(result)
예
// %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)< : (te>nsor2x2x<i1, ten>sor2x2xi<32, ten>sor>2x2xi32<) - ten>sor2x2xi32
// %result: [[5, 2], [3, 8]]
select_and_scatter
시맨틱스
select를 사용하여 input 텐서의 reduce_window 결과를 기반으로 scatter를 사용하여 source 텐서의 값을 분산하고 result 텐서를 생성합니다.
다음 다이어그램은 구체적인 예를 사용하여 operand 및 source에서 result의 요소가 계산되는 방식을 보여줍니다.
더 공식적으로 표현하면 다음과 같습니다.
selected_values = reduce_window_without_init(...)를 다음 입력으로 실행합니다.inputs = [operand].window_dimensions,window_strides,padding은 있는 그대로 사용됩니다.base_dilations = windows_dilations = 1.body은 다음과 같이 정의됩니다.
def body(arg0: tensor<E>, arg1: tensor<E>) -> tensor<E>: return select(arg0, arg1) ? arg0 : arg1;여기서
E = element_type(operand)와reduce_window_without_init는 기본reduce의schedule(reduce 참고)에 init 값이 포함되지 않는다는 점을 제외하고reduce_window와 정확히 동일하게 작동합니다. 현재 해당 창에 값이 없는 경우 어떻게 되는지는 지정되어 있지 않습니다(#731).result[result_index] = reduce([source_values], [init_value], [0], scatter)각 항목의 의미는 다음과 같습니다.source_values = [source[source_index] for source_index in source_indices].selected_values[source_index]에operand_index의operand요소가 있는 경우selected_index(source_index) = operand_indexsource_indices = [source_index for source_index in indices(source) if selected_index(source_index) = result_index].
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1~C4), (C6), (C8~C11) |
| (I2) | source |
텐서 또는 텐서별 양자화 텐서 | (C1), (C2) |
| (I3) | init_value |
0차원 텐서 또는 텐서별 양자화 텐서 | (C3) |
| (I4) | window_dimensions |
si64 유형의 1차원 텐서 상수 |
(C2), (C4), (C5) |
| (I5) | window_strides |
si64 유형의 1차원 텐서 상수 |
(C2), (C6), (C7) |
| (I6) | padding |
si64 유형의 2차원 텐서 상수 |
(C2), (C8) |
| (I7) | select |
함수 | (C9) |
| (I8) | scatter |
함수 | (C10) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C11-C12) |
제약조건
- (C1)
element_type(operand) = element_type(source). - (C2)
shape(source) = num_windows여기서:padded_operand_shape = padding[:, 0] + shape(operand) + padding[:, 1].is_empty_window = padded_operand_shape = 0 || window_dimensions > padded_operand_shape.num_windows = is_empty_window ? 0 : floor((padded_operand_shape - window_dimensions) / window_strides) + 1.
- (C3)
element_type(init_value) = element_type(operand). - (C4)
size(window_dimensions) = rank(operand) - (C5)
0 < window_dimensions. - (C6)
size(window_strides) = rank(operand) - (C7)
0 < window_strides. - (C8)
shape(padding) = [rank(operand), 2] - (C9)
select의 유형은(tensor<E>, tensor<E>) -> tensor<i1>이며 여기서E = element_type(operand)입니다. - (C10)
scatter의 유형은(tensor<E>, tensor<E>) -> tensor<E>이며 여기서is_promotable(element_type(operand), E)입니다. - (C11)
shape(operand) = shape(result). - (C12)
element_type(result) = E.
예
// %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(%ar<g0:> tensori64, %ar<g1:> tensori64):
%0 = "stablehlo.compare"(%arg0, %arg1) {
comparison_di<rection = #stablehlocom>parison_directio<n G>E
} <: (>ten>sori64,< t>ensori64) - tensori1
"stable<hl>o.r>eturn"(%0) : (tensori1) <- (>)
}, {
^bb0(%<arg>0: tensori64, %arg1: tensori64):
%0 = "sta<ble>hlo.add&<quo>t;(>%arg0, <%ar>g1) : (tensori64, tensori64) - tensor<i64>
> "stablehlo.return"(%0) :< (tensori>64) - ()
}) {
window_dim<ensions => arrayi64: 3, 1,
<window_strides => arrayi64<: 2, 1,>
padding =< dense[>[0, 1], <[0, 0]]> : tenso<r2x>2xi>64
} : <(tensor>4x2xi64, tensor2x2xi64, tensori64) - tensor4x2xi64
// %result: [[0, 0], [0, 0], [5, 14], [7, 0]]
보내기
시맨틱스
채널 channel_id에 inputs을 전송합니다. 그런 다음 입력이 source_target_pairs에 지정된 순서대로 다른 기기로 전송됩니다. 이 작업은 result 토큰을 생성합니다.
is_host_transfer이 true이면 작업이 호스트로 데이터를 전송합니다. 그렇지 않으면 source_target_pairs 값을 기반으로 다른 기기로 데이터를 전송합니다. 이 플래그는 channel_type에 제공된 정보를 중복하므로 향후에는 둘 중 하나만 유지할 계획입니다(#666). is_host_transfer = false이고 source_target_pairs가 None이거나 비어 있으면 정의되지 않은 동작으로 간주됩니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | inputs |
가변 개수의 텐서 또는 양자화된 텐서 | |
| (I2) | token |
token |
|
| (I3) | channel_id |
si64 유형의 상수 |
|
| (I4) | channel_type |
DEVICE_TO_DEVICE 및 DEVICE_TO_HOST의 열거형 |
(C5) |
| (I5) | is_host_transfer |
i1 유형의 상수 |
(C5~C6) |
| (I6) | source_target_pairs |
si64 유형의 2차원 텐서 상수 |
(C1~C4), (C6) |
출력
| 이름 | 유형 |
|---|---|
result |
token |
제약조건
- (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, 여기서N은 다음과 같이 정의됩니다.cross_replica이 사용된 경우num_replicascross_partition이 사용된 경우num_partitions
- (C5)
channel_type은 다음과 같이 정의됩니다.is_host_transfer = true인 경우DEVICE_TO_HOST- 그 밖의 경우에는
DEVICE_TO_DEVICE입니다.
예
%result = "stablehlo.send"(%operand, %token) {
channel_handle = #stablehlo.chan<nel_handlehandle = 0>, type = 1,
is_host_transfer = false,
source_target_pai<rs = dense[[0, 1>], [1, 2]<] : ten>sor2x2xi64
}< : (ten>sor2x2xi64, !stablehl>o.token) - !stablehlo.token
shift_left
시맨틱스
lhs 텐서에 대해 요소별 왼쪽 시프트 연산을 rhs 비트 수만큼 실행하고 result 텐서를 생성합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
정수형 텐서 | (C1) |
| (I2) | rhs |
정수형 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수형 텐서 | (C1) |
제약조건
- (C1)
type(lhs) = type(rhs) = type(result).
예
// %lhs: [-1, 0, 1]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_left"(%lhs, %rhs<): (t>ensor3xi<64, t>ens>or3xi64<) - t>ensor3xi64
// %result: [-2, 0, 8]
shift_right_arithmetic
시맨틱스
lhs 텐서에 rhs 비트 수만큼 요소별 산술 오른쪽 시프트 연산을 실행하고 result 텐서를 생성합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
정수형 텐서 | (C1) |
| (I2) | rhs |
정수형 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수형 텐서 | (C1) |
제약조건
- (C1)
type(lhs) = type(rhs) = type(result).
예
// %lhs: [-1, 0, 8]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_right_arithmetic"(%lhs, %rhs<): (t>ensor3xi<64, t>ens>or3xi64<) - t>ensor3xi64
// %result: [-1, 0, 1]
shift_right_logical
시맨틱스
lhs 텐서에 대해 비트 수 rhs만큼 요소별 논리 오른쪽 시프트 연산을 수행하고 result 텐서를 생성합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
정수형 텐서 | (C1) |
| (I2) | rhs |
정수형 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수형 텐서 | (C1) |
제약조건
- (C1)
type(lhs) = type(rhs) = type(result).
예
// %lhs: [-1, 0, 8]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_right_logical"(%lhs, %rhs<): (t>ensor3xi<64, t>ens>or3xi64<) - t>ensor3xi64
// %result: [9223372036854775807, 0, 1]
서명
시맨틱스
operand 요소의 부호를 반환하고 result 텐서를 생성합니다.
더 공식적으로 각 요소 x의 의미는 Python 문법을 사용하여 다음과 같이 표현할 수 있습니다.
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)))
양자화된 유형의 경우 dequantize_op_quantize(sign, operand, type(result))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부호 있는 정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부호 있는 정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// 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)< : (t>ens>or5xf64<) - t>ensor5xf64
// Logical values: +NaN, -1.0, -0.0, +0.0, 1.0
// %result: [0x7FFFFFFFFFFFFFFF, -1.0, -0.0, 0.0, 1.0]
사인
시맨틱스
operand 텐서에 요소별 사인 연산을 수행하여 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
sin - 복소수의 경우: 복소수 사인입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(sine, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.sine"(%operand)< : (ten>sor>2x2xf32<) - ten>sor2x2xf32
// %result: [[0.0, 1.0], [0.0, -1.0]]
slice
시맨틱스
정적으로 계산된 시작 색인을 사용하여 operand에서 슬라이스를 추출하고 result 텐서를 생성합니다. start_indices에는 각 차원의 슬라이스 시작 색인이 포함되고, limit_indices에는 각 차원의 슬라이스 종료 색인(해당 값 제외)이 포함되며, strides에는 각 차원의 스트라이드가 포함됩니다.
더 공식적으로는 result[result_index] = operand[operand_index]입니다. 여기서 operand_index = start_indices + result_index * strides입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 텐서별 양자화 텐서 | (C1~C3), (C5) |
| (I2) | start_indices |
si64 유형의 1차원 텐서 상수 |
(C2), (C3), (C5) |
| (I3) | limit_indices |
si64 유형의 1차원 텐서 상수 |
(C2), (C3), (C5) |
| (I4) | strides |
si64 유형의 1차원 텐서 상수 |
(C2), (C4) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 텐서별 양자화 텐서 | (C1), (C5) |
제약조건
- (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).
예
// %operand: [
// [0, 0, 0, 0],
// [0, 0, 1, 1],
// [0, 0, 1, 1]
// ]
%result = "stablehlo.slice"(%operand) {
start_indic<es = arra>yi64: 1, 2,
limit_indic<es = arra>yi64: 3, 4,
strid<es = arra>yi64: 1, 1
}< : (ten>sor>3x4xi64<) - ten>sor2x2xi64
// % result: [
// [1, 1],
// [1, 1]
// ]
정렬
시맨틱스
comparator에 따라 dimension 측정기준을 따라 inputs의 1차원 슬라이스를 함께 정렬하고 results를 생성합니다.
다른 작업의 유사한 입력과 달리 dimension는 아래에 설명된 시맨틱스를 사용하여 음수 값을 허용합니다. 향후 일관성 문제로 인해 허용되지 않을 수 있습니다(#1377).
is_stable이 true이면 정렬이 안정적입니다. 즉, 비교기에서 동일하다고 간주되는 요소의 상대적 순서가 유지됩니다. 입력이 하나인 경우 두 요소 e1와 e2는 comparator(e1, e2) = comparator(e2, e1) = false인 경우에만 비교기에 의해 동일한 것으로 간주됩니다. 이것이 여러 입력으로 일반화되는 방법은 아래의 공식화를 참고하세요.
좀 더 공식적으로 index_space(results[0])의 모든 result_index에 대해 다음이 적용됩니다.
adjusted_dimension = dimension >= 0 ? dimension : rank(inputs[0]) + dimension.result_slice = [ri0, ..., :, ..., riR-1](여기서riN은result_index의 개별 요소이고:은adjusted_dimension에 삽입됨)inputs_together = (inputs[0]..., ..., inputs[N-1]...).results_together[result_slice] = sort(inputs_together[result_slice], comparator_together).- 여기서
sort는 1차원 슬라이스를 내림차순이 아닌 순서로 정렬하며,comparator_together는 왼쪽 인수가 오른쪽 두 번째 인수보다 작으면true를 반환할 것으로 예상합니다. 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.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | inputs |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C1~C5) |
| (I2) | dimension |
si64 유형의 상수 |
(C4) |
| (I3) | is_stable |
i1 유형의 상수 |
|
| (I4) | comparator |
함수 | (C5) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 텐서 또는 텐서별 양자화된 텐서 | (C2), (C3) |
제약조건
- (C1)
0 < size(inputs). - (C2)
type(inputs...) = type(results...) - (C3)
same(shape(inputs...) + shape(results...)). - (C4)
-R <= dimension < R(R = rank(inputs[0])) - (C5)
comparator의 유형은(tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>이고 여기서Ei = element_type(inputs[i])입니다.
예
// %input0 = [[1, 2, 3], [3, 2, 1]]
// %input1 = [[3, 2, 1], [1, 2, 3]]
%result0, %result1 = "stablehlo.sort"(%input0, %input1) ({
^bb0(%ar<g0:> tensori64, %ar<g1:> tensori64, %ar<g2:> tensori64, %ar<g3:> tensori64):
%predicate = "stablehlo.compare"(%arg0, %arg1) {
comparison_di<rection = #stablehlocom>parison_directio<n G>T
} <: (>ten>sori64,< t>ensori64) - tensori1
"stablehlo.retu<rn>&qu>ot;(%predicate) : (tensori1) - ()
}) {
dimension = 0 : i64,
< is_st>able = t<rue
} :> (t>ensor2x3<xi64, t>ensor2x3<xi64) -> (tensor2x3xi64, tensor2x3xi64)
// %result0 = [[3, 2, 3], [1, 2, 1]]
// %result1 = [[1, 2, 1], [3, 2, 3]]
sqrt
시맨틱스
operand 텐서에 대해 요소별 제곱근 연산을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
squareRoot - 복소수의 경우 복소수 제곱근입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(sqrt, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [[0.0, 1.0], [4.0, 9.0]]
%result = "stablehlo.sqrt"(%operand)< : (ten>sor>2x2xf32<) - ten>sor2x2xf32
// %result: [[0.0, 1.0], [2.0, 3.0]]
subtract
시맨틱스
두 텐서 lhs와 rhs의 요소별 뺄셈을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 정수의 경우: 정수 뺄셈
- 부동 소수점의 경우: IEEE-754의
subtraction - 복소수의 경우 복소수 뺄셈입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(subtract, lhs, rhs, type(result)).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
| (I2) | rhs |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
정수, 부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).
예
// %lhs: [[6, 8], [10, 12]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.subtract"(%lhs, %rhs)< : (ten>sor2x2xf<32, ten>sor>2x2xf32)< - (ten>sor2x2xf32)
// %result: [[1, 2], [3, 4]]
tan
시맨틱스
operand 텐서에 요소별 탄젠트 연산을 수행하여 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
tan - 복소수의 경우 복소수 탄젠트입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(tan, operand, type(result))
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.tan"(%operand)< : (ten>sor>2x2xf64<) - ten>sor2x2xf64
// %result: [
// [0.0, 1.63312e+16],
// [0.0, 5.44375e+15]
// ]
tanh
시맨틱스
operand 텐서에 요소별 쌍곡선 탄젠트 연산을 수행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 부동 소수점의 경우: IEEE-754의
tanh - 복소수의 경우: 복소수 하이퍼볼릭 탄젠트입니다.
- 양자화된 유형의 경우:
dequantize_op_quantize(tanh, operand, type(result)).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_type(operand) = baseline_type(result).
예
// %operand: [-1.0, 0.0, 1.0]
%result = "stablehlo.tanh"(%operand)< : (t>ens>or3xf32<) - t>ensor3xf32
// %result: [-0.76159416, 0.0, 0.76159416]
전치
시맨틱스
permutation를 사용하여 operand 텐서의 차원을 순열하고 result 텐서를 생성합니다. 더 공식적으로는 result[result_index] = operand[operand_index]입니다. 여기에서 result_index[d] = operand_index[permutation[d]]입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
텐서 또는 양자화된 텐서 | (C1~C4) |
| (I2) | permutation |
si64 유형의 1차원 텐서 상수 |
(C2-C4) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
텐서 또는 양자화된 텐서 | (C1), (C3-C4) |
제약조건
- (C1)
element_type(result)은 다음 식으로 주어집니다.!is_per_axis_quantized(operand)인 경우element_type(operand)element_type(operand)와 동일합니다. 단,quantization_dimension(operand)및quantization_dimension(result)는 다를 수 있습니다.
- (C2)
permutation은range(rank(operand))의 순열입니다. - (C3)
shape(result) = dim(operand, permutation...). - (C4)
is_per_axis_quantized(result)인 경우quantization_dimension(operand) = permutation(quantization_dimension(result))입니다.
예
// %operand: [
// [[1,2], [3,4], [5,6]],
// [[7,8], [9,10], [11,12]]
// ]
%result = "stablehlo.transpose"(%operand) {
permutati<on = arrayi6>4: 2, 1, 0
}< : (tenso>r2x>3x2xi32<) - tenso>r2x3x2xi32
// %result: [
// [[1,7], [3,9], [5,11]],
// [[2,8], [4,10], [6,12]]
// ]
triangular_solve
시맨틱스
하삼각 또는 상삼각 계수 행렬을 사용하여 연립일차방정식의 배치를 풉니다.
더 공식적으로 a와 b가 주어지면 result[i0, ..., iR-3, :, :]는 left_side가 true일 때 op(a[i0, ..., iR-3, :, :]) * x = b[i0, ..., iR-3, :, :]의 해답이고 left_side가 false일 때 x * op(a[i0, ..., iR-3, :, :]) = b[i0, ..., iR-3, :, :]의 해답입니다. 여기서 op(a)는 transpose_a에 의해 결정되고 transpose_a는 다음 중 하나일 수 있는 변수 x에 대해 해결합니다.
NO_TRANSPOSE:a을 있는 그대로 사용하여 작업을 실행합니다.TRANSPOSE:a의 전치에 대한 연산을 실행합니다.ADJOINT:a의 공액 전치에 대한 연산을 실행합니다.
lower이 true인 경우 a의 하단 삼각형에서 입력 데이터를 읽고, 그렇지 않으면 a의 상단 삼각형에서 입력 데이터를 읽습니다. 출력 데이터는 동일한 삼각형에 반환됩니다. 다른 삼각형의 값은 구현에 따라 정의됩니다.
unit_diagonal가 true이면 구현은 a의 대각선 요소가 1이라고 가정할 수 있습니다. 그렇지 않으면 동작이 정의되지 않습니다.
양자화된 유형의 경우 dequantize_op_quantize(lambda x, y: triangular_solve(x, y, left_side, lower,
unit_diagonal, transpose_a), a, b, type(result))를 실행합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | a |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1-C3) |
| (I2) | b |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1~C4) |
| (I3) | left_side |
i1 유형의 상수 |
(C3) |
| (I4) | lower |
i1 유형의 상수 |
|
| (I5) | unit_diagonal |
i1 유형의 상수 |
|
| (I6) | transpose_a |
NO_TRANSPOSE, TRANSPOSE, ADJOINT의 enum |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 또는 복소수 유형의 텐서 또는 텐서별 양자화 텐서 | (C1) |
제약조건
- (C1)
baseline_element_type(a) = baseline_element_type(b). - (C2)
2 <= rank(a) = rank(b) = R - (C3)
shape(a)와shape(b)의 관계는 다음과 같이 정의됩니다.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)
예
// %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 = <#stablehlotranspose NO>_TRANSPOSE
}< : (ten>sor3x3xf<32, ten>sor>3x3xf32<) - ten>sor3x3xf32
// %result: [
// [2.0, 0.0, 0.0],
// [0.0, 2.0, 0.0],
// [0.0, 0.0, 2.0]
// ]
tuple
시맨틱스
값 val에서 result 튜플을 생성합니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | val |
가변 개수의 값 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
tuple | (C1) |
제약조건
- (C1)
result의 유형은tuple<E0, ..., EN-1>이며 여기서Ei = type(val[i])입니다.
예
// %val0: memref[1.0, 2.0]
// %val1: (3)
%result = "stablehlo.tuple"(%val0, %val1)< : (m>emref2x<f32, t<upl>>ete>nsori3<2) - t<uplem>emref2x<f32, t<upl>>>etensori32
// %result: (memref[1.0, 2.0], (3))
uniform_dequantize
시맨틱스
양자화된 텐서 operand를 operand 유형으로 정의된 양자화 매개변수에 따라 부동 소수점 텐서 result로 요소별 변환을 실행합니다.
더 공식적으로는 result = dequantize(operand)입니다.
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
양자화된 텐서 | (C1), (C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
부동 소수점 유형의 텐서 | (C1), (C2) |
제약조건
- (C1)
shape(operand) = shape(result). - (C2)
element_type(result) = expressed_type(operand)
예
// %operand: [10, 10]
%result = "stablehlo.uniform_dequantize"(%operand)< : (tensor2x!qua<nt.uniformi8:f32:0, {0.1:-3>>0,0>.5:-20}<) - t>ensor2xf32
// %result: [4.0, 15.0]
uniform_quantize
시맨틱스
result 유형으로 정의된 양자화 매개변수에 따라 부동 소수점 텐서 또는 양자화 텐서 operand를 양자화 텐서 result로 요소별 변환을 실행합니다.
더 공식적으로 말하면
is_float(operand)인 경우:result = quantize(operand, type(result)).
is_quantized(operand)인 경우:float_result = dequantize(operand).result = quantize(float_result, type(result)).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
부동 소수점 또는 양자화 유형의 텐서 | (C1), (C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
양자화된 텐서 | (C1), (C2) |
제약조건
- (C1)
shape(operand) = shape(result). - (C2)
expressed_type(result) = is_float(operand) ? element_type(operand) : expressed_type(operand)
예
// %operand: [4.0, 15.0]
%result = "stablehlo.uniform_quantize"(%operand)< : (t>ens>or2xf32<) - tensor2x!qua<nt.uniformi8:f32:0, {0.1:-3>>0,0.5:-20}
// %result: [10, 10]
// %operand: [10, 10]
%result = "stablehlo.uniform_quantize"<(%operand) : (te<nsor2x!quant.uniformi8:f32:>>0, >{0.1:-3<0,0.5:-20}) - te<nsor2x!quant.uniformi8:f32:>>0, {0.1:-20,0.2:-30}
// %result: [20, 45]
반면
시맨틱스
cond 함수가 true를 출력하는 동안 body 함수를 0회 이상 실행한 결과를 생성합니다. 더 공식적으로는 시맨틱을 Python 구문을 사용하여 다음과 같이 표현할 수 있습니다.
internal_state = operand
while cond(*internal_state):
internal_state = body(*internal_state)
results = internal_state
무한 루프의 동작은 미정입니다(#383).
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | operand |
가변 개수의 값 | (C1-C3) |
| (I2) | cond |
함수 | (C1) |
| (I3) | body |
함수 | (C2) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
results |
가변 개수의 값 | (C3) |
제약조건
- (C1)
cond의 유형은(T0, ..., TN-1) -> tensor<i1>이며, 여기서Ti = type(operand[i])입니다. - (C2)
body의 유형은(T0, ..., TN-1) -> (T0, ..., TN-1)이며, 여기서Ti = type(operand[i])입니다. - (C3)
type(results...) = type(operand...).
예
// %init_i: 1
// %init_sum: 0
// %one: 1
// %ten: 10
%results0, %results1 = "stablehlo.while"(%init_i, %init_sum) ({
^bb0(%ar<g0:> tensori64, %ar<g1:> tensori64):
%cond = "stablehlo.compare"(%arg0, %ten) {
comparison_di<rection = #stablehlocom>parison_directio<n L>T
} <: (>ten>sori64,< t>ensori64) - tensori1
stablehlo.r<et>urn %cond : tensori1
}, {
< ^>bb0(%arg0: tens<ori>64, %arg1: tensori64):
%new_sum = stablehlo.add <%ar>g1, %one : tensori64
%new_i = stablehlo.add <%ar>g0, %one : tensori64
stablehlo.return %new_<i, >%new_sum< : >tensori64, te<nso>ri64
}) <: (>ten>sori64, <ten>sori64) <- (>tensori64, tensori64)
// %results0: 10
// %results1: 10
xor
시맨틱스
두 텐서 lhs와 rhs의 요소별 XOR을 수행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.
- 불리언: 논리적 XOR
- 정수의 경우: 비트 XOR
입력
| 라벨 | 이름 | 유형 | 제약조건 |
|---|---|---|---|
| (I1) | lhs |
불리언 또는 정수 유형의 텐서 | (C1) |
| (I2) | rhs |
불리언 또는 정수 유형의 텐서 | (C1) |
출력
| 이름 | 유형 | 제약조건 |
|---|---|---|
result |
불리언 또는 정수 유형의 텐서 | (C1) |
제약조건
- (C1)
type(lhs) = type(rhs) = type(result).
예
// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.xor"(%lhs, %rhs)< : (ten>sor2x2xi<32, ten>sor>2x2xi32<) - ten>sor2x2xi32
// %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>2x2>xi1, te<nsor2x>2xi1) - tensor2x2xi1
// %result: [[false, true], [true, false]]
언어 상호 운용성
현재 StableHLO 프로그램에는 StableHLO로 정의되지 않은 작업이 포함되는 경우가 있습니다.
모듈, 함수, 호출 및 반환
StableHLO는 ModuleOp, FuncOp, CallOp, ReturnOp에 업스트림 MLIR 작업을 사용합니다. 이는 기존 MLIR 머신과의 더 나은 상호 운용성을 위해 실행되었습니다. 유용한 패스가 FuncOp 및 ModuleOp을 타겟팅하여 작성되고 많은 컴파일 파이프라인에서 이러한 작업이 존재할 것으로 예상하기 때문입니다. 이러한 작업에는 완전한 호환성 보장이 적용됩니다. 호환되지 않는 방식으로 이러한 작업이 변경되는 경우 (즉, 삭제) 호환성을 유지하기 위해 StableHLO 상응 항목이 추가됩니다.
CHLO
CHLO opset에는 StableHLO로 분해되는 상위 수준 작업이 포함되어 있습니다. 현재 CHLO의 호환성은 보장되지 않습니다. 호환성을 보장하려면 직렬화 전에 chlo-legalize-to-stablehlo 패스를 사용해야 합니다.
모양 작업
커뮤니티에서는 동적 StableHLO 프로그램에서 핵심 MLIR 다이얼렉트의 특정 작업을 사용하여 모양 계산을 실행하는 것이 일반적인 사용 사례입니다.
가장 일반적으로는 shape_of 또는 num_elements와 같은 shape 다이얼렉트 작업, dim 또는 from_elements와 같은 tensor 다이얼렉트 작업, 내장 index 유형이 포함됩니다.
동적 RFC > O2에서는 이를 범위 외로 표시하지만 상호 운용성을 위해 index 유형에 대한 일부 지원이 포함됩니다. 이러한 작업이나 유형의 호환성은 보장되지 않습니다. shape-legalize-to-stablehlo 패스를 사용하여 이러한 작업을 완전히 지원되는 StableHLO 작업으로 변환할 수 있습니다.
지원 중단된 작업
MHLO에서 상속된 StableHLO 작업 중 지원 중단되었으며 StableHLO에서 삭제될 예정인 작업이 여러 개 있습니다. 이러한 삭제에 관한 자세한 내용은 StableHLO v1.0 정리 #2283에서 확인할 수 있습니다. 이 지원 중단에 대한 추적기 문제는 #2340입니다.
이러한 작업은 몇 가지 카테고리로 분류됩니다.
- StableHLO 작업의 'HLO에 없음' 카테고리 - 처음에는 StableHLO opset에 포함되었지만 나중에 적합하지 않은 것으로 간주되었습니다.
broadcast,create_token,cross-replica-sum,dot,einsum,torch_index_select,unary_einsum(#3) - 사용되지 않는 작업 - 이러한 작업은 한때 유용했을 수 있지만 작업이 미개발되었거나 이러한 작업을 사용하는 파이프라인이 더 이상 필요하지 않도록 리팩터링되었습니다. 여기에는
map,tuple(#598),get_tuple_element,rng,complex비교(#560), 컨볼루션window_reversal(#1181)이 포함됩니다.
이러한 작업 중 일부는 기존 작업 (broadcast, create_token, cross-replica-sum, dot, unary_einsum)을 사용하여 표현할 수 있으므로 쉽게 삭제할 수 있으며 기존 호환성 기간 (6개월)이 지나면 삭제됩니다. 삭제를 검토 중인 다른 작업도 있습니다 (einsum, get_tuple_element, map, rng, torch_index_select, tuple, complex 비교, window_reversal). 커뮤니티 의견에 따라 이러한 작업은 삭제되거나 완전히 지원되는 사양에 추가됩니다. 이러한 작업의 미래가 알려질 때까지는 6개월의 호환성만 보장됩니다.
실행
순차 실행
StableHLO 프로그램은 main 함수에 입력 값을 제공하고 출력 값을 계산하여 실행됩니다. 함수의 출력 값은 해당 return 작업에 루트가 있는 작업 그래프를 실행하여 계산됩니다.
실행 순서는 데이터 흐름과 일치하는 한 구현 정의입니다. 즉, 작업이 사용 전에 실행되는 경우입니다. StableHLO에서 부작용이 있는 모든 작업은 하나의 토큰을 소비하고 하나의 토큰을 생성합니다 (after_all를 통해 여러 토큰을 하나의 토큰으로 멀티플렉싱할 수 있음). 따라서 부작용의 실행 순서도 데이터 흐름과 정렬됩니다. 예를 들어 아래 프로그램에는 %0 → %1 → %2 → return 및 %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>
}
더 공식적으로 StableHLO 프로세스는 1) StableHLO 프로그램, 2) 작업 상태 (아직 실행되지 않음, 이미 실행됨), 3) 프로세스가 작업 중인 중간 값의 조합입니다.
이 프로세스는 main 함수의 입력 값으로 시작하여 작업 상태와 중간 값을 업데이트하는 작업 그래프를 거쳐 출력 값으로 끝납니다. 추가 형식화는 미정입니다(#484).
동시 실행
StableHLO 프로그램은 ui32 유형인 num_replicas x num_partitions의 2D 프로세스 그리드로 구성되어 병렬로 실행될 수 있습니다.
StableHLO 프로세스 그리드에서 StableHLO 프로세스 num_replicas * num_partitions개가 동시에 실행됩니다. 각 프로세스에는 고유한 process_id = (replica_id, partition_id)가 있습니다. 여기서 replica_ids = range(num_replicas)의 replica_id와 partition_ids = range(num_partitions)의 partition_id는 모두 ui32 유형입니다.
프로세스 그리드의 크기는 모든 프로그램에 대해 정적으로 알려져 있으며 (향후 StableHLO 프로그램의 명시적 부분으로 만들 계획임 #650) 프로세스 그리드 내 위치는 모든 프로세스에 대해 정적으로 알려져 있습니다. 각 프로세스는 replica_id 및 partition_id 작업을 통해 프로세스 그리드 내 위치에 액세스할 수 있습니다.
프로세스 그리드 내에서 프로그램은 모두 동일할 수도 있고('단일 프로그램, 다중 데이터' 스타일) 모두 다를 수도 있으며('다중 프로그램, 다중 데이터' 스타일) 그 중간일 수도 있습니다. 향후 GSPMD를 비롯한 병렬 StableHLO 프로그램을 정의하는 다른 관용구를 지원할 계획입니다 (#619).
프로세스 그리드 내에서 프로세스는 대부분 서로 독립적입니다. 별도의 작업 상태, 별도의 입력/중간/출력 값이 있으며 대부분의 작업은 아래에 설명된 소수의 집합 작업을 제외하고 프로세스 간에 별도로 실행됩니다.
대부분의 작업 실행은 동일한 프로세스의 값만 사용하므로 일반적으로 이름으로 이러한 값을 참조하는 것이 명확합니다.
하지만 집단 연산의 의미를 설명할 때는 충분하지 않으며, 특정 프로세스 내에서 값 name을 참조하는 표기법 name@process_id가 발생합니다. (이 관점에서 볼 때, 자격이 없는 name는 name@(replica_id(), partition_id())의 약어로 볼 수 있습니다.)
프로세스 간 실행 순서는 아래에 설명된 대로 포인트 투 포인트 통신 및 집단 작업으로 도입된 동기화를 제외하고 구현에 따라 정의됩니다.
점대점 통신
StableHLO 프로세스는 StableHLO 채널을 통해 서로 통신할 수 있습니다. 채널은 si64 유형의 양수 ID로 표시됩니다. 다양한 작업을 통해 채널에 값을 전송하고 채널에서 값을 수신할 수 있습니다.
이러한 채널 ID가 어디에서 오는지, 프로세스 프로그램이 이를 어떻게 인식하는지, 어떤 종류의 동기화가 도입되는지 등 추가적인 공식화는 미정입니다(TBD)(#484).
스트리밍 커뮤니케이션
모든 StableHLO 프로세스는 다음 두 스트리밍 인터페이스에 액세스할 수 있습니다.
- 읽을 수 있는 인피드입니다.
- 쓸 수 있는 아웃피드
프로세스 간에 통신하는 데 사용되므로 양쪽 끝에 프로세스가 있는 채널과 달리 infeeds와 outfeeds의 다른 쪽 끝은 구현에 따라 정의됩니다.
스트리밍 통신이 실행 순서에 어떤 영향을 미치는지, 어떤 종류의 동기화가 도입되는지 등 추가 공식화는 미정입니다(TBD)(#484).
집단 작업
StableHLO에는 all_gather, all_reduce, all_to_all, collective_broadcast, collective_permute, reduce_scatter의 6가지 집단 연산이 있습니다. 이러한 모든 작업은 StableHLO 프로세스 그리드의 프로세스를 StableHLO 프로세스 그룹으로 분할하고 각 프로세스 그룹 내에서 다른 프로세스 그룹과 독립적으로 공동 계산을 실행합니다.
각 프로세스 그룹 내에서 집단 작업은 동기화 장벽을 도입할 수 있습니다. 이 동기화가 정확히 언제 발생하는지, 프로세스가 이 장벽에 정확히 어떻게 도달하는지, 도달하지 않으면 어떻게 되는지 등 추가적인 공식화는 미정입니다(TBD)(#484).
프로세스 그룹에 파티션 ID가 다른 프로세스가 있는 등 프로세스 그룹에 교차 파티션 통신이 포함되는 경우 집단 작업 실행에 채널이 필요하며 집단 작업은 si64 유형의 양수 channel_id를 제공해야 합니다. 크로스 복제본 통신에는 채널이 필요하지 않습니다.
집단 연산으로 실행되는 계산은 개별 연산에 따라 다르며 위의 개별 연산 섹션에 설명되어 있습니다. 하지만 프로세스 그리드가 프로세스 그룹으로 분할되는 전략은 이러한 작업 간에 공유되며 이 섹션에 설명되어 있습니다. 더 공식적으로 StableHLO는 다음 네 가지 전략을 지원합니다.
cross_replica
각 프로세스 그룹 내에서만 교차 복제본 통신이 발생합니다. 이 전략은 replica_groups(복제본 ID 목록의 목록)를 가져와 replica_groups와 partition_ids의 데카르트 곱을 계산합니다. replica_groups에는 고유한 요소가 있어야 하며 모든 replica_ids를 포함해야 합니다. 더 공식적으로 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
예를 들어 replica_groups = [[0, 1], [2, 3]] 및 num_partitions = 2의 경우 cross_replica는 [[(0, 0), (1, 0)], [(0, 1), (1, 1)], [(2, 0), (3, 0)], [(2, 1), (3, 1)]]를 생성합니다.
cross_partition
파티션 간 통신은 각 프로세스 그룹 내에서만 발생합니다. 이 전략은 partition_groups(파티션 ID 목록의 목록)를 가져와 partition_groups와 replica_ids의 데카르트 곱을 계산합니다.
partition_groups에는 고유한 요소가 있어야 하며 모든 partition_ids를 포함해야 합니다.
더 공식적으로 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
예를 들어 partition_groups = [[0, 1]] 및 num_replicas = 4의 경우 cross_partition는 [[(0, 0), (0, 1)], [(1, 0), (1, 1)], [(2, 0), (2, 1)], [(3, 0), (3, 1)]]를 생성합니다.
cross_replica_and_partition
크로스 복제본 통신과 크로스 파티션 통신은 모두 각 프로세스 그룹 내에서 발생할 수 있습니다. 이 전략은 replica_groups(복제본 ID 목록의 목록)를 가져와 각 replica_group의 데카르트 곱을 partition_ids로 계산합니다. replica_groups에는 고유한 요소가 있어야 하며 모든 replica_ids를 포함해야 합니다. 더 공식적으로 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
예를 들어 replica_groups = [[0, 1], [2, 3]] 및 num_partitions = 2의 경우 cross_replica_and_partition는 [[(0, 0), (1, 0), (0, 1), (1, 1)], [(2, 0), (3, 0), (2, 1), (3, 1)]]를 생성합니다.
flattened_ids
이 전략은 flattened_id_groups(replica_id * num_partitions + partition_id 형식의 '병합된' 프로세스 ID 목록)를 가져와 프로세스 ID로 변환합니다. flattened_id_groups에는 고유한 요소가 있어야 하며 모든 process_ids를 포함해야 합니다. 더 공식적으로 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
예를 들어 flattened_id_groups = [[0, 1, 2, 3], [4, 5, 6, 7]], num_replicas = 4, num_partitions = 2의 경우 flattened_ids는 [[(0, 0), (0, 1), (1, 0), (1, 1)], [(2, 0), (2, 1), (3, 0), (3, 1)]]를 생성합니다.
정확성
현재 StableHLO는 숫자 정확도를 보장하지 않지만 향후 변경될 수 있습니다(#1156).
양자화된 작업의 실행 시맨틱스
양자화된 StableHLO 작업의 해석은 하드웨어 요구사항 및 기능에 따라 달라질 수 있습니다. 예를 들어 일부 하드웨어는 '역양자화, 부동 소수점 연산 실행, 최종 양자화' 전략을 사용하여 양자화된 연산을 해석할 수 있습니다. 일부는 정수 산술 연산으로 전체 계산을 실행할 수 있습니다. 따라서 양자화된 StableHLO 작업의 해석은 특정 구현에 의해서만 결정됩니다. 하이브리드 양자화(#1575)의 해석은 사양에 명시된 의미론 (1792를 통해)을 기반으로 해야 합니다.
오류
StableHLO 프로그램은 개별 작업에 대한 광범위한 제약 조건을 통해 검증되므로 런타임 전에 많은 오류 클래스가 제외됩니다. 하지만 정수 오버플로, 범위를 벗어난 액세스 등을 통해 오류 조건이 발생할 수 있습니다. 명시적으로 언급되지 않는 한 이러한 모든 오류는 구현 정의 동작을 초래하지만 이는 향후 변경될 수 있습니다 (#1157).
부동 소수점 예외
이 규칙의 예외로 StableHLO 프로그램의 부동 소수점 예외는 잘 정의된 동작을 갖습니다. IEEE-754 표준에 정의된 예외 (잘못된 작업, 0으로 나누기, 오버플로, 언더플로 또는 부정확한 예외)가 발생하는 작업은 기본 결과 (표준에 정의됨)를 생성하고 해당 상태 플래그를 발생시키지 않고 실행을 계속합니다. 표준의 raiseNoFlag 예외 처리와 유사합니다. 비표준 작업 (예: 복잡한 산술 및 특정 초월 함수)의 예외는 구현에 따라 정의됩니다.
모양 불일치
StableHLO는 동적 형태 텐서를 지원합니다. 하지만 런타임에 모양이 일치해야 합니다. 그렇지 않으면 동작이 정의되지 않습니다. StableHLO는 런타임에 텐서가 지정된 모양을 갖는다고 어설션할 수 있는 작업을 명시적으로 제공하지 않습니다. 올바른 코드를 생성하는 것은 생산자의 책임입니다.
구체적인 예로 아래 프로그램은 유효합니다. 하지만 런타임에 %arg0과 %arg1의 정확한 모양은 동일해야 합니다. 그렇지 않으면 프로그램의 동작이 정의되지 않습니다.
func.func @foo(%arg0: tensor<?xi32>, %arg1: tensor<?xi32>) -> tensor<?xi32> {
%0 = stablehlo.add %arg0, %arg1 : tensor<?xi32>
return %0 : tensor<?xi32>
}
Notation
구문을 설명하기 위해 이 문서에서는 수정된 ISO 버전의 EBNF 구문 (ISO/IEC 14977:1996, Wikipedia)을 사용합니다. 두 가지 수정사항이 있습니다. 1) 규칙은 =이 아닌 ::=를 사용하여 정의됩니다.
2) 연결은 , 대신 병치로 표현됩니다.
의미론을 설명하기 위해('유형', '상수', '작업' 섹션 내) 아래에 설명된 대로 배열 작업을 간결하게 표현하는 지원 기능으로 확장된 Python 문법을 기반으로 하는 공식을 사용합니다. 이 방법은 작은 코드 스니펫에 적합하지만, 큰 코드 스니펫이 필요한 드문 경우에는 항상 명시적으로 도입되는 일반 Python 구문을 사용합니다.
수식
dot_general 사양의 예를 바탕으로 수식이 어떻게 작동하는지 살펴보겠습니다. 이 작업의 제약 조건 중 하나는 다음과 같습니다.
dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...)
이 수식에 사용된 이름은 두 가지 소스에서 가져옵니다. 1) 전역 함수(예: dim), 2) 해당 프로그램 요소의 멤버 정의(예: dot_general의 '입력' 섹션에 정의된 lhs, lhs_batching_dimensions, rhs, rhs_batching_dimensions 입력)
위에서 언급한 대로 이 수식의 구문은 간결성 지향 확장 프로그램이 있는 Python 기반입니다. 수식을 이해하기 위해 일반 Python 문법으로 변환해 보겠습니다.
A) 이 공식에서는 =를 사용하여 등식을 나타내므로 Python 구문을 얻기 위한 첫 번째 단계는 다음과 같이 =를 ==로 바꾸는 것입니다.dim(lhs, lhs_batching_dimensions...) == dim(rhs, rhs_batching_dimensions...)
B) 또한 이러한 수식은 스칼라 표현식을 텐서 표현식으로 변환하는 줄임표 (...)를 지원합니다. 간단히 말해 f(xs...)는 '텐서 xs의 각 스칼라 x에 대해 스칼라 f(x)를 계산한 다음 이러한 모든 스칼라 결과를 텐서 결과로 함께 반환'을 의미합니다. 일반 Python 구문에서 예시 수식은 [dim(lhs, dim1) for dim1 in lhs_batching_dimensions] ==
[dim(rhs, dim2) for dim2 in rhs_batching_dimensions]로 바뀝니다.
타원 덕분에 개별 스칼라 수준에서 작업하지 않아도 되는 경우가 많습니다. 하지만 까다로운 경우에는 gather 사양의 start_indices[bi0, ..., :, ..., biN] 공식과 같이 하위 수준의 준공식 구문이 사용될 수 있습니다. 간결성을 위해 이러한 구문을 일반 Python으로 변환하는 정확한 형식주의는 제공하지 않습니다. 사례별로 직관적으로 이해할 수 있기를 바랍니다.
특정 수식이 불투명해 보이면 알려주세요. 개선해 보겠습니다.
또한 텐서, 텐서 목록 (예: 가변 텐서 수에서 발생할 수 있음) 등 모든 종류의 목록을 확장하는 데 타원이 사용됩니다. 이는 정확한 형식을 제공하지 않는 또 다른 영역입니다 (예: 목록은 StableHLO 유형 시스템의 일부도 아님). 대신 직관적인 이해 가능성에 의존합니다.
C) 마지막으로 주목할 만한 표기법 차량은 암시적 브로드캐스팅입니다. StableHLO opset은 암시적 브로드캐스팅을 지원하지 않지만 수식은 간결성을 위해 지원합니다. 간단히 말해 텐서가 예상되는 컨텍스트에서 스칼라가 사용되면 스칼라가 예상되는 모양으로 브로드캐스트됩니다.
dot_general 예시를 계속해서 살펴보면 또 다른 제약 조건은 다음과 같습니다.
0 <= lhs_batching_dimensions < rank(lhs) dot_general 사양에 정의된 대로 lhs_batching_dimensions는 텐서이지만 0와 rank(lhs)는 스칼라입니다. 암시적 브로드캐스팅을 적용하면 수식이 [0, ..., 0] <= lhs_batching_dimensions < [rank(lhs), ..., rank(lhs)]이 됩니다.
특정 dot_general 작업에 적용하면 이 공식은 불리언 텐서로 평가됩니다. 수식이 제약 조건으로 사용되는 경우 수식이 true 또는 true 요소만 있는 텐서로 평가되면 제약 조건이 유지됩니다.
이름
수식에서 어휘 범위에는 1) 전역 함수, 2) 멤버 정의,
3) 로컬 정의 전역 함수 목록은 아래에 제공됩니다. 요소 정의 목록은 표기법이 적용되는 프로그램 요소에 따라 다릅니다.
- 작업의 경우 멤버 정의에는 '입력' 및 '출력' 섹션에 도입된 이름이 포함됩니다.
- 그 외의 모든 경우, 멤버 정의에는 프로그램 요소의 구조적 부분이 포함되며, 해당 EBNF 비터미널의 이름을 따릅니다. 대부분의 경우 이러한 구조적 부분의 이름은 비터미널의 이름을 스네이크 케이스로 변환하여 가져오지만 (예:
IntegerLiteral=>integer_literal) 때로는 이 과정에서 이름이 약어로 표시됩니다 (예:QuantizationStorageType=>storage_type). 이 경우 작업 사양의 '입력' / '출력' 섹션과 유사하게 이름이 명시적으로 도입됩니다. - 또한 멤버 정의에는 항상
self이 포함되어 해당 프로그램 요소를 참조합니다.
값
수식을 평가할 때 다음 유형의 값과 함께 작동합니다.
1) Value (실제 값, 예: dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>, 항상 유형을 알고 있음)
2) Placeholder (미래 값, 예: lhs, rhs 또는 result, 실제 값은 아직 알 수 없으며 유형만 알고 있음)
3) Type('유형' 섹션에 정의된 유형)
4) Function('함수' 섹션에 정의된 전역 함수)
컨텍스트에 따라 이름이 다른 값을 참조할 수 있습니다. 더 구체적으로 말하면 작업의 '의미' 섹션 (및 기타 프로그램 요소의 상응하는 섹션)은 런타임 로직을 정의하므로 모든 입력은 Value로 사용할 수 있습니다.
반면, 작업 (및 이에 상응하는 항목)의 '제약 조건' 섹션은 '컴파일 시간' 논리를 정의합니다. 즉, 일반적으로 런타임 전에 실행되는 항목이므로 상수 입력만 Value로 사용할 수 있고 다른 입력은 Placeholder로만 사용할 수 있습니다.
| 이름 | '의미론' | '제약 조건' |
|---|---|---|
| 전역 함수 | Function |
Function |
| 상수 입력 | Value |
Value |
| 상수가 아닌 입력 | Value |
Placeholder |
| 출력 | Value |
Placeholder |
| 로컬 정의 | 정의에 따라 다름 | 정의에 따라 다름 |
transpose 작업의 예를 살펴보겠습니다.
%result = "stablehlo.transpose"(%operand) {
permutati<on = dens>e[2, 1, 0<] : t>ensor3xi64
}< : (tenso>r2x>3x2xi32<) - tenso>r2x3x2xi32
이 작업에서 permutation는 상수이므로 의미 체계와 제약 조건 모두에서 Value로 사용할 수 있습니다. 반면 operand 및 result은 의미상 Value로 사용할 수 있지만 제약 조건에서는 Placeholder로만 사용할 수 있습니다.
함수
유형 구성
유형을 구성하는 데 사용할 수 있는 함수가 없습니다. 대신 일반적으로 더 간결한 유형 문법을 직접 사용합니다. 예를 들어 function_type(
[tensor_type([], E), tensor_type([], E)], [tensor_type([], E)]) 대신 (tensor<E>, tensor<E>) -> (tensor<E>)를 사용합니다.
유형의 함수
element_type는 텐서 유형과 양자화된 텐서 유형에 정의되며 각각 해당TensorType또는QuantizedTensorType의TensorElementType또는QuantizedTensorElementType부분을 반환합니다.
def element_type(x: Value | Placeholder | Type):
if type(x) == TensorType:
return tensor_element_type(x)
if type(x) == QuantizedTensorType:
return quantized_tensor_element_type(x)
if type(x) is not Type:
return element_type(type(x))
is_per_axis_quantized(x: Value | Placeholder | Type) -> Value은is_quantized(x) and quantization_dimension(x) is not None의 단축키입니다.is_per_tensor_quantized(x: Value | Placeholder | Type) -> Value은is_quantized(x) and quantization_dimension(x) is None의 바로가기입니다.is_promotable(x: Type, y: Type) -> bool는 유형x을 유형y로 승격할 수 있는지 확인합니다.x및y이QuantizedTensorElementType인 경우 프로모션은storage_type에만 적용됩니다. 이 특정 버전의 프로모션은 현재 할인 계산 컨텍스트에서 사용됩니다 (자세한 내용은 RFC 참고).
def is_promotable(x: Type, y: Type) -> Value:
is_same_type = (is_bool(x) and is_bool(y)) or
(is_integer(x) and is_integer(y)) or (is_float(x) and is_float(y)) or
(is_complex(x) and is_complex(y)) or
(is_quantized(x) and is_quantized(y) and expressed_type(x) = expressed_type(y))
if is_same_type == False:
return False
if is_integer(x) or is_float(x):
return bitwidth(x) <= bitwidth(y)
if is_complex(x):
return bitwidth(element_type(x)) <= bitwidth(element_type(y))
if is_quantized(x):
return bitwidth(storage_type(x)) <= bitwidth(storage_type(y))
return false
is_quantized(x: Value | Placeholder | Type) -> Value은is_quantized_tensor_element_type(x)의 단축키입니다.is_type_name(x: Value | Placeholder | Type) -> Value: 모든 유형에 사용할 수 있습니다. 예를 들어x이FloatType이면is_float(x)는true을 반환합니다.x가 값 또는 자리표시자인 경우 이 함수는is_type_name(type(x))의 바로가기입니다.max_value(x: Type) -> Value은TensorElementType의 최댓값을 반환합니다.x이TensorElementType이 아니면None를 반환합니다.min_value(x: Type) -> Value는TensorElementType의 최소 가능 값을 반환합니다.x이TensorElementType이 아니면None를 반환합니다.member_name(x: Value | Placeholder | Type) -> Any. 모든 유형의 모든 멤버 정의member_name에 사용할 수 있습니다. 예를 들어tensor_element_type(x)은 해당TensorType의TensorElementType부분을 반환합니다.x가 값 또는 자리표시자인 경우 이 함수는member_name(type(x))의 바로가기입니다.x가 적절한 멤버가 있는 유형이거나 이러한 유형의 값 또는 자리표시자가 아닌 경우None을 반환합니다.is_empty_algorithm(*args: Type)은 모든 점 알고리즘 필드가None로 설정되었는지 확인합니다. 점 알고리즘에는 구현 정의 기본 동작이 있으므로 기본값을 지정하는 것은 잘못됩니다.
값 구성
operation_name(*xs: Value | Type) -> Value. 모든 작업에 사용할 수 있습니다. 예를 들어add(lhs, rhs)는 두 텐서 값lhs와rhs를 가져와 이러한 입력으로add연산을 평가한 결과를 반환합니다.broadcast_in_dim와 같은 일부 작업의 경우 출력 유형이 '하중 지지'입니다. 즉, 작업을 평가하는 데 필요합니다. 이 경우 함수는 이러한 유형을 인수로 사용합니다.
값에 대한 함수
모든 Python 연산자와 함수를 사용할 수 있습니다. 예를 들어 Python의 구독 및 슬라이싱 표기법을 모두 사용하여 텐서, 양자화된 텐서, 튜플을 색인화할 수 있습니다.
to_destination_type(x: Value, destination_type: Type) -> Value는 텐서에 정의되며type(x)및destination_type에 따라x의 변환된 값을 다음과 같이 반환합니다.
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)
convert, uniform_quantize, uniform_dequantize 작업 병합에 관한 초기 논의가 있습니다 (#1576).
병합 후에는 위의 함수가 필요하지 않으며 convert에 작업 이름을 대신 사용할 수 있습니다.
is_nan(x: Value) -> Value는 텐서에 정의되며x의 모든 요소가NaN이면true를 반환하고 그렇지 않으면false를 반환합니다.x이 텐서가 아니면None을 반환합니다.is_sorted(x: Value) -> Value는 텐서에 정의되며,x의 요소가 색인의 오름차순 사전순에 따라 오름차순으로 정렬되면true을 반환하고 그렇지 않으면false을 반환합니다.x이 텐서가 아니면None을 반환합니다.is_unique(x: Value) -> Value는 텐서에 정의되며x에 중복 요소가 없으면true를 반환하고 그렇지 않으면false를 반환합니다.x이 텐서가 아니면None을 반환합니다.member_name(x: Value) -> Any는 모든 값의 모든 멤버 정의member_name에 대해 정의됩니다. 예를 들어real_part(x)은 해당ComplexConstant의RealPart부분을 반환합니다.x에 적절한 구성원이 있는 값이 아니면None을 반환합니다.same(x: Value) -> Value는 텐서에 정의되며x의 요소가 모두 서로 같으면true를 반환하고 그렇지 않으면false를 반환합니다. 텐서에 요소가 없으면 '모두 서로 같음'으로 간주되므로 함수는true를 반환합니다.x이 텐서가 아니면None을 반환합니다.split(x: Value, num_results: Value, axis: Value) -> Value는 텐서에 정의되며 축axis를 따라x의num_results슬라이스를 반환합니다.x이 텐서 또는dim(x, axis) % num_results != 0이 아니면None를 반환합니다.is_defined_in_parent_scope(x: Value) -> Value는 문자열에 정의되며x가 관련 작업의 상위 함수와 동일한 범위에 정의된 함수의 이름인 경우true를 반환합니다.is_namespaced_op_name(x: Value) -> Value는 문자열에 정의되며x이 유효한 작업 이름인 경우, 즉 다음 정규 표현식을 준수하는 경우true을 반환합니다.[a-zA-Z][a-zA-Z0-9_]*([.][a-zA-Z0-9_$]+)+
모양 계산
axes(x: Value | Placeholder | Type) -> Value은range(rank(x))의 단축키입니다.dim(x: Value | Placeholder | Type, axis: Value) -> Value은shape(x)[axis]의 단축키입니다.dims(x: Value | Placeholder | Type, axes: List) -> List은list(map(lambda axis: dim(x, axis), axes))의 단축키입니다.index_space(x: Value | Placeholder | Type) -> Value은 텐서에 정의되며, 오름차순 사전순으로 정렬된 해당TensorType의size(x)색인을 반환합니다(예:[0, ..., 0],[0, ..., 1], ...,shape(x) - 1).x가 텐서 유형, 양자화된 텐서 유형 또는 이러한 유형 중 하나의 값이나 자리표시자가 아니면None을 반환합니다.rank(x: Value | Placeholder | Type) -> Value은size(shape(x))의 단축키입니다.shape(x: Value | Placeholder | Type) -> Value는member_name를 통해 '유형의 함수' 섹션에 정의됩니다.size(x: Value | Placeholder | Type) -> Value은reduce(lambda x, y: x * y, shape(x))의 단축키입니다.
양자화 계산
def baseline_element_type(x: Value | Placeholder | Type) -> Type은element_type(baseline_type(x))의 바로가기입니다.baseline_type는 텐서 유형과 양자화된 텐서 유형에 정의되어 있으며 이를 '기준'으로 변환합니다. 즉, 모양은 동일하지만 요소 유형의 양자화 매개변수가 기본값으로 재설정된 유형입니다. 이는 텐서와 양자화된 텐서 유형을 모두 균일하게 비교하는 데 유용한 트릭으로 사용되며, 이는 꽤 자주 필요합니다. 양자화된 유형의 경우 이를 통해 양자화 매개변수를 무시하고 유형을 비교할 수 있습니다. 즉,shape,storage_type,expressed_type,storage_min,storage_max,quantization_dimension(축별 양자화 유형의 경우)은 모두 일치해야 하지만scales및zero points는 다를 수 있습니다.
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는 양자화 텐서 유형에 정의되어 이를 부동 소수점 텐서 유형으로 변환합니다. 이는 저장소 유형의 정수 값을 나타내는 양자화된 요소를 양자화된 요소 유형과 연결된 0점과 스케일을 사용하여 표현된 유형의 해당 부동 소수점 값으로 변환하여 발생합니다.
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는 부동 소수점 텐서 유형에 정의되며 이를 양자화된 텐서 유형으로 변환합니다. 이는 표현된 유형의 부동 소수점 값을 양자화된 요소 유형과 연결된 0점과 스케일을 사용하여 저장 유형의 해당 정수 값으로 변환하여 이루어집니다.
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는 양자화된 텐서에 대한 요소별 계산을 지정하는 데 사용됩니다. 양자화된 요소를 표현된 유형으로 변환한 후 작업을 실행하고 결과를 다시 저장 유형으로 변환합니다. 현재 이 함수는 텐서별 양자화에만 작동합니다. 축별 양자화는 진행 중인 작업입니다(#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는 부동 소수점의 lhs와 양자화된 유형의 rhs를 허용하는 하이브리드 작업의 가중치 전용 양자화를 지정하는 데 사용됩니다. 양자화된 입력을 표현된 유형으로 역양자화하고 부동 소수점으로 계산을 실행합니다. 부동 소수점 lhs 텐서의 요소 유형과 양자화된 rhs 텐서의 표현된 유형이 동일해야 합니다.
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))
그리드 계산
cross_partition(replica_groups: Value) -> Value. 위의 'cross_replica' 섹션을 참고하세요.cross_replica(replica_groups: Value) -> Value. 위의 'cross_replica' 섹션을 참고하세요.cross_replica_and_partition(replica_groups: Value) -> Value. 위의 'cross_replica_and_partition' 섹션을 참고하세요.flattened_ids(replica_groups: Value) -> Value. 위의 'flattened_ids' 섹션을 참고하세요.
역동성
StableHLO 값은 동적 차원 크기를 가질 수 있습니다(예: tensor<?xi64>).
하지만 StableHLO 값은 동적 차원 수 (순위가 지정되지 않은 동적, 예: tensor<*xi64>)를 가질 수 없습니다. 피연산자와 결과는 크기에 제약이 있더라도 동적 차원 크기를 사용할 수 있습니다. 가능한 경우 제약 조건은 정적으로 확인되고, 그렇지 않으면 런타임으로 지연되며 불일치로 인해 정의되지 않은 동작이 발생합니다. 아래에서 예를 확인하세요.
단항 요소별 작업의 모양 불일치
다음과 같은 간단한 프로그램을 고려해 보세요.
func.func @foo(%arg0: tensor<?xf64>) {
%0 = stablehlo.abs %arg0 : (tensor<?xf64>) -> tensor<2xf64>
return
}
결과의 모양은 알지만 입력의 모양은 알지 못하는 것은 일반적이지 않으므로 이러한 프로그램은 특이합니다. 그럼에도 불구하고 이는 유효한 StableHLO 프로그램입니다. 피연산자의 정확한 모양을 알 수 없으므로 이 프로그램에서 abs 작업을 정적으로 검증할 수 없습니다. 하지만 모양은 확실히 호환되며 정적으로 확인할 수 있습니다. 런타임에 ?이 2이 될 수 있으며 문제가 없습니다. 하지만 ?가 다른 정수일 수도 있으며, 이 경우 동작이 정의되지 않습니다.
결과에서 측정기준 크기가 동적인 경우 정의되지 않은 동작이 있을 수 없습니다. 실제로 '예상' 크기가 없으므로 불일치가 있을 수 없습니다.
바이너리 요소별 작업의 모양 불일치
다음과 같은 간단한 프로그램을 고려해 보세요.
func.func @foo(%arg0: tensor<?xf64>, %arg1: tensor<?xf64>) {
%0 = stablehlo.add %arg0, %arg0 : (tensor<?xf64>, tensor<?xf64>) -> tensor<?xf64>
return
}
이진 요소별 연산의 경우 입력과 결과의 모양이 런타임에 일치해야 합니다. 컴파일 시간에 정적 차원은 동일해야 합니다. 그렇지 않으면 호환되기만 하면 됩니다. 입력에서 하나라도 동적인 측정기준이 있으면 런타임에 정의되지 않은 동작이 발생할 수 있습니다. 동적 크기가 다른 피연산자 (정적이든 동적이든)의 해당 크기와 일치하지 않을 수 있기 때문입니다. 모든 입력이 정적인 경우 결과가 동적인지 여부는 중요하지 않습니다. 정적으로 알려진 측정기준은 정적으로 확인되고 동적 측정기준은 제약을 부과하지 않습니다.
출력 모양을 피연산자로 사용하는 작업의 모양 불일치
다음과 같은 간단한 프로그램을 고려해 보세요.
func.func @foo(%arg0: tensor<2xi32>) {
%0 = stablehlo.dynamic_iota %arg0, dim = 0 : (tensor<2xi32>) -> tensor<3x4xi64>
return
}
런타임의 모양 피연산자의 값은 결과의 모양과 일치해야 합니다. 그렇지 않으면 동작이 정의되지 않습니다. 즉, 런타임에 %arg0의 값이 dense<[3, 4]> : tensor<2xi32>이어야 합니다. 모양 피연산자가 상수인 경우 정적으로 확인할 수 있습니다. 결과 모양이 완전히 동적인 경우 불일치가 있을 수 없습니다.