StableHLO 사양

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 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
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의 경우 해당하는 부동 소수점 값 ff = (i - zero_point) * scale로 계산할 수 있으며, 여기서 scalezero_point정규화 매개변수라고 합니다. storage_minstorage_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는 부동 소수점 상수이지만 승수와 이동으로 표현되는 정수 기반 배율에 많은 관심이 있습니다. 가까운 시일 내에 이를 살펴볼 계획입니다. (#1404)

유형, 값, 양자화 텐서 유형에 0점이 하나만 있거나 여러 개 있을 수 있는지 여부 등 QuantizationZeroPoint의 의미 체계에 대한 논의가 계속되고 있습니다. 이 논의의 결과에 따라 0점에 관한 사양은 향후 변경될 수 있습니다. (#1405)

진행 중인 또 다른 논의는 이러한 값과 정량화된 텐서의 값에 제약 조건을 적용해야 하는지 결정하기 위한 QuantizationStorageMinQuantizationStorageMax의 시맨틱스와 관련이 있습니다(#1406).

마지막으로, 알 수 없는 크기를 나타내는 방법 (#1407)과 마찬가지로 알 수 없는 크기를 나타내는 방법을 살펴볼 계획입니다.

양자화 텐서 유형은 양자화 요소가 있는 텐서를 나타냅니다. 이러한 텐서는 요소에 일반 요소 유형 대신 정량화된 요소 유형이 있다는 점을 제외하고 일반 텐서와 정확히 동일합니다.

정규화된 텐서에서 정규화는 텐서별일 수 있습니다. 즉, 전체 텐서에 하나의 scalezero_point가 있거나 축별일 수 있습니다. 즉, 특정 크기 quantization_dimension의 슬라이스당 하나씩 여러 개의 scaleszero_points가 있을 수 있습니다. 더 공식적으로 축당 양자화를 사용하는 텐서 t에는 quantization_dimensiondim(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)입니다.
TokenType ::= 'token'

토큰 유형은 토큰을 나타냅니다. 즉, 일부 작업에서 생성되고 소비되는 불투명한 값입니다. 토큰은 실행 섹션에 설명된 대로 작업에 실행 순서를 적용하는 데 사용됩니다.

TupleType ::= 'tuple' '<' TupleElementTypes '>'
TupleElementTypes ::= [ValueType {',' ValueType}]

튜플 유형은 튜플, 즉 이종 목록을 나타냅니다. 튜플은 HLO와의 호환성을 위해만 존재하는 기존 기능입니다. HLO에서 튜플은 가변 입력과 출력을 나타내는 데 사용됩니다. StableHLO에서는 가변 입력과 출력이 기본적으로 지원되며 StableHLO에서 튜플을 사용하는 유일한 목적은 HLO ABI를 포괄적으로 나타내는 것입니다. 예를 들어 T, tuple<T>, tuple<tuple<T>>는 특정 구현에 따라 실질적으로 다를 수 있습니다. 향후 StableHLO에서 튜플 유형을 삭제할 수 있는 HLO ABI를 변경할 계획입니다(#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차원 텐서 값으로 표현하는 것이 관례입니다.

  • 불리언 유형은 불리언 값 truefalse을 나타냅니다.
  • 정수 유형은 부호 있는(si) 유형 또는 부호 없는(ui) 유형일 수 있으며 지원되는 비트 너비(2, 4, 8, 16, 32 또는 64) 중 하나를 가질 수 있습니다. 부호 있는 siN 유형은 -2^(N-1)2^(N-1)-1 사이의 정수 값(양 끝값 포함)을 나타내고 부호 없는 uiN 유형은 02^N-1 사이의 정수 값(양 끝값 포함)을 나타냅니다.
  • 부동 소수점 유형은 다음 중 하나일 수 있습니다.
  • 복소수 유형은 동일한 요소 유형실부허수를 가진 복합 값을 나타냅니다. 지원되는 복합 유형은 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 연산자에서 추론됨).

입력 함수의 구문에는 MLIR와의 호환성을 위해 현재 사용되지 않는 부분(위의 Unused 프로덕션 참고)이 포함되어 있습니다. MLIR에는 점프 작업을 통해 함께 연결된 여러 작업 '블록'을 포함할 수 있는 보다 일반적인 '리전' 개념이 있습니다. 이러한 블록에는 Unused 프로덕션에 해당하는 ID가 있으므로 서로 구분할 수 있습니다. StableHLO에는 점프 연산자가 없으므로 MLIR 문법의 해당 부분은 사용되지 않지만 여전히 존재합니다.

OpInputAttr      ::= OpInputAttrName '=' OpInputAttrValue
OpInputAttrName  ::= letter {letter | digit}
OpInputAttrValue ::= Constant

입력 속성에는 이름과 지원되는 상수 중 하나인 값이 있습니다. 프로그램 요소의 정적 메타데이터를 지정하는 기본 방법입니다. 예를 들어 concatenate 연산자는 dimension 속성을 사용하여 입력 값이 연결되는 측정기준을 지정합니다. 마찬가지로 slice 작업은 start_indiceslimit_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 연산자의 경우 출력 유형을 입력에서 추론할 수 있기 때문). 그럼에도 불구하고 op 서명은 MLIR와의 호환성을 위해 의도적으로 StableHLO 문법의 일부입니다.

다음은 mnemonik이 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'

부울 상수는 불리언 값 truefalse를 나타냅니다. 불리언 상수의 유형은 i1입니다.

IntegerConstant   ::= IntegerLiteral ':' IntegerType
IntegerLiteral    ::= ['-' | '+'] DecimalDigits
                    | ['-' | '+'] '0x' HexadecimalDigits
DecimalDigits     ::= decimalDigit {decimalDigit}
HexadecimalDigits ::= hexadecimalDigit {hexadecimalDigit}
decimalDigit      ::= '0' | ... | '9'
hexadecimalDigit  ::= decimalDigit | 'a' | ... | 'f' | 'A' | ... | 'F'

정수 상수는 십진수 또는 16진수 표기법을 사용하는 문자열을 통해 정수 값을 나타냅니다. 다른 기수(예: 바이너리 또는 옥텟)는 지원되지 않습니다. 정수 상수에는 다음과 같은 제약이 있습니다.

  • (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 텐서에서 요소별 절대값 연산을 실행하고 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) : (tensor<3xi32>) -> tensor<3xi32>
// %result: [2, 0, 2]

 예시 더보기

추가

시맨틱

두 텐서 lhsrhs의 요소별 추가를 수행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.

  • 불리언: 논리합(OR)
  • 정수의 경우 정수 덧셈
  • 부동 소수점의 경우: 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).
  • 작업에서 정규화된 텐서를 사용하는 경우:
    • (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)입니다.

// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.add"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[6, 8], [10, 12]]

예시 더보기

after_all

시맨틱

inputs를 생성하는 작업이 result에 종속된 작업 전에 실행되도록 합니다. 이 작업을 실행해도 아무 일도 일어나지 않습니다. 이 작업은 result에서 inputs로의 데이터 종속 항목을 설정하기 위한 용도로만 존재합니다.

입력

라벨 이름 유형
(I1) inputs token의 가변수

출력

이름 유형
result token

// %input0: !stablehlo.token
// %input1: !stablehlo.token
%result = "stablehlo.after_all"(%input0, %input1) : (!stablehlo.token, !stablehlo.token) -> !stablehlo.token

 예시 더보기

all_gather

시맨틱

StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 all_gather_dim을 따라 각 프로세스의 operands 텐서 값을 연결하고 results 텐서를 생성합니다.

이 작업은 StableHLO 프로세스 그리드를 다음과 같이 정의된 process_groups로 분할합니다.

  • channel_id <= 0 and use_global_device_ids = false인 경우 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 = true인 경우 flattened_ids(replica_groups)입니다.

그런 다음 각 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_replicas
    • cross_replica_and_partition가 사용된 경우 num_replicas
    • flattened_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_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
  // channel_id = 0
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
  // use_global_device_ids = false
} : (tensor<2x2xi64>, tensor<2x2xi64>) -> (tensor<2x4xi64>, tensor<2x4xi64>)
// %result0@(0, 0): [[1, 2, 5, 6], [3, 4, 7, 8]]
// %result0@(1, 0): [[1, 2, 5, 6], [3, 4, 7, 8]]
// %result1@(0, 0): [[11, 12, 15, 16], [13, 14, 17, 18]]
// %result1@(1, 0): [[11, 12, 15, 16], [13, 14, 17, 18]]

예시 더보기

all_reduce

시맨틱

StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 각 프로세스의 operands 텐서 값에 감소 함수 computation를 적용하고 results 텐서를 생성합니다.

이 작업은 StableHLO 프로세스 그리드를 다음과 같이 정의된 process_groups로 분할합니다.

  • channel_id <= 0 and use_global_device_ids = false인 경우 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 = true인 경우 flattened_ids(replica_groups)입니다.

그런 다음 각 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_replicas
    • cross_replica_and_partition가 사용된 경우 num_replicas
    • flattened_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(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
  // channel_id = 0
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
  // use_global_device_ids = false
} : (tensor<4xi64>, tensor<4xi64>) -> (tensor<4xi64>, tensor<4xi64>)
// %result0@(0, 0): [6, 8, 10, 12]
// %result0@(1, 0): [6, 8, 10, 12]
// %result1@(0, 0): [22, 24, 26, 28]
// %result1@(1, 0): [22, 24, 26, 28]

예시 더보기

all_to_all

시맨틱

all_to_all

StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 split_dimension을 따라 operands 텐서의 값을 부분으로 분할하고, 분할된 부분을 프로세스 간에 흩어 뿌리고, 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)
  • scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group], 여기서 receiver_index = process_group.index(receiver).
  • 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_replicas
    • cross_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_groups = dense<[[0, 1]]> : tensor<1x2xi64>
  // channel_id = 0
} : (tensor<2x4xi64>, tensor<2x4xi64>) -> (tensor<4x2xi64>, tensor<4x2xi64>)
// %result#0@(0, 0): [[1, 2], [5, 6], [9, 10], [13, 14]]
// %result#0@(1, 0): [[3, 4], [7, 8], [11, 12], [15, 16]]
// %result#1@(0, 0): [[17, 18], [21, 22], [25, 26], [29, 30]]
// %result#1@(1, 0): [[19, 20], [23, 24], [27, 28], [31, 32]]

 예시 더보기

시맨틱

두 텐서 lhsrhs의 요소별 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) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[1, 2], [3, 0]]

예시 더보기

atan2

시맨틱

lhsrhs 텐서에서 요소별 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) : (tensor<3xf64>, tensor<3xf64>) -> tensor<3xf64>
// %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_scalegrad_offsetbaseline_element_type가 동일합니다.
  • (C3) operand, grad_output, grad_operand의 모양이 동일합니다.
  • (C4) scale, mean, variance, grad_scalegrad_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
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>,
     tensor<2x2x2xf64>) -> (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %grad_operand: [
//                 [[0.0, 0.0], [0.0, 0.0]],
//                 [[0.0, 0.0], [0.0, 0.0]]
//                ]
// %grad_scale:  [0.0, 0.0]
// %grad_offset: [0.4, 0.4]

batch_norm_inference

시맨틱

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, resultbaseline_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
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>) -> tensor<2x2x2xf64>
// %result: [
//           [[0.0, 0.0], [2.0, 2.0]],
//           [[2.0, 2.0], [0.0, 0.0]]
//          ]

batch_norm_training

시맨틱

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
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>) ->
    (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %output: [
//           [[0.0, 0.0], [2.0, 2.0]],
//           [[2.0, 2.0], [0.0, 0.0]]
//          ]
// %batch_mean: [2.0, 3.0]
// %batch_var: [1.0, 1.0]

bitcast_convert

시맨틱

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.
    • 모든 0 <= i < Rdim(result, i) = dim(operand, i)
    • dim(result, R) * num_bits(E') = num_bits(E).
    • num_bits(E') > num_bits(E)인 경우:
    • rank(result) = R - 1.
    • 모든 0 <= i < Rdim(result, i) = dim(operand, i)
    • 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) : (tensor<f64>) -> tensor<4xf16>
// %result: [0xCDEF, 0x89AB, 0x4567, 0x0123] // little-endian representation

 예시 더보기

broadcast_in_dim

시맨틱

operand 텐서의 데이터를 복제하여 입력 텐서의 크기 또는 순위를 확장하고 result 텐서를 생성합니다. 더 엄밀히 말해 axes(operand)의 모든 d에 대해 result[result_index] = operand[operand_index]입니다.

  • 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_dimensions = array<i64: 2, 1>
} : (tensor<1x3xi32>) -> tensor<2x3x2xi32>
// %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, %result_branch0) : (tensor<2xi64>, tensor<2xi64>) -> ()
}, {
  "stablehlo.return"(%result_branch1, %result_branch1) : (tensor<2xi64>, tensor<2xi64>) -> ()
}) : (tensor<i32>) -> (tensor<2xi64>, tensor<2xi64>)
// %result0: [1, 1]
// %result1: [1, 1]

 예시 더보기

cbrt

시맨틱

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) : (tensor<4xf64>) -> tensor<4xf64>
// %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) : (tensor<5xf32>) -> tensor<5xf32>
// %result: [-0.0, -0.0, 1.0, 1.0, 2.0]

예시 더보기

콜레스키

시맨틱

일련의 행렬의 콜레스키 분해를 계산합니다.

더 엄밀히 말해 index_space(result)의 모든 i에 대해 result[i0, ..., iR-3, :, :]a[i0, ..., iR-3, :, :]의 콜레스키 분해이며, 이 분해는 하삼각형(lowertrue인 경우) 또는 상삼각형(lowerfalse인 경우) 행렬의 형태를 취합니다. 반대 삼각형의 출력 값(즉, 엄격한 상위 삼각형 또는 엄격한 하위 삼각형)은 구현에 의해 정의됩니다.

입력 행렬이 허미티안 양의 정규 행렬이 아닌 i가 있는 경우 동작은 정의되지 않습니다.

정규화된 유형의 경우 dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result))를 실행합니다.

입력

라벨 이름 유형 제약조건
(I1) a 부동 소수점 또는 복합 유형의 텐서 또는 텐서별 양자화 텐서 (C1~C3)
(I2) lower i1 유형의 0차원 텐서 상수

출력

이름 유형 제약조건
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
} : (tensor<3x3xf32>) -> tensor<3x3xf64>
// %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) : (tensor<3xi32>, tensor<3xi32>, tensor<3xi32>) -> tensor<3xi32>
// %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_replicas
    • cross_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_groups = dense<[[2, 1]]> : tensor<1x2xi64>,
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor1x2xi64>) -> tensor<1x2xi64>
// %result@(0, 0): [[0, 0]]
// %result@(1, 0): [[5, 6]]
// %result@(2, 0): [[5, 6]]
// %result@(3, 0): [[0, 0]]

collective_permute

시맨틱

StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 소스 프로세스에서 타겟 프로세스로 operand 텐서의 값을 전송하고 result 텐서를 생성합니다.

이 작업은 StableHLO 프로세스 그리드를 다음과 같이 정의된 process_groups로 분할합니다.

  • channel_id <= 0인 경우 cross_replica(source_target_pairs)
  • channel_id > 0인 경우 cross_partition(source_target_pairs)입니다.

그 후 result@process는 다음과 같이 주어집니다.

  • process_groups[i, 1] = process와 같은 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 텐서 또는 텐서별 양자화 텐서 (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_replicas
    • cross_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_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>,
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor<2x2xi64>) -> tensor<2x2xi64>
//
// %result@(0, 0): [[0, 0], [0, 0]]
// %result@(1, 0): [[1, 2], [3, 4]]
// %result@(2, 0): [[5, 6], [7, 8]]

예시 더보기

compare

시맨틱

comparison_directioncompare_type에 따라 lhsrhs 텐서의 요소별 비교를 실행하고 result 텐서를 생성합니다.

comparison_directioncompare_type 값은 다음과 같은 시맨틱을 갖습니다.

부울 및 정수 요소 유형의 경우:

  • EQ: lhs = rhs.
  • NE: lhs != rhs.
  • GE: lhs >= rhs.
  • GT: lhs > rhs.
  • LE: lhs <= rhs.
  • LT: lhs < rhs.

compare_type = FLOAT가 있는 부동 소수점 요소 유형의 경우 이 작업은 다음 IEEE-754 연산을 구현합니다.

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

compare_type = TOTALORDER가 있는 부동 소수점 요소 유형의 경우 작업은 IEEE-754의 totalOrdercompareQuietEqual 연산 조합을 사용합니다.

복잡한 요소 유형의 경우 제공된 comparison_directioncompare_type를 사용하여 (real, imag) 쌍의 사전식 비교가 수행됩니다. 복소수에 정렬을 적용할 때는 놀라운 시맨틱스가 필요하기 때문에 앞으로 comparison_directionGE, 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의 열거형
(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))인 경우 SIGNED
    • is_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs))인 경우 UNSIGNED
    • FLOAT 또는 TOTALORDER(is_float(element_type(lhs))인 경우)
    • is_complex(element_type(lhs))인 경우 FLOAT입니다.

// %lhs: [1.0, 3.0]
// %rhs: [1.1, 2.9]
%result = "stablehlo.compare"(%lhs, %rhs) {
  comparison_direction = #stablehlo<comparison_direction LT>,
  compare_type = #stablehlo<comparison_type FLOAT>
} : (tensor<2xf32>, tensor<2xf32>) -> tensor<2xi1>
// %result: [true, false]

 예시 더보기

복잡함

시맨틱

실수와 허수 값 쌍인 lhsrhs에서 복소수 값으로 요소별 변환을 실행하고 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) : (tensor<2xf64>, tensor<2xf64>) -> tensor<2xcomplex<f64>>
// %result: [(1.0, 2.0), (3.0, 4.0)]

 예시 더보기

복합

시맨틱

다른 StableHLO 작업으로 구성된 작업을 캡슐화하여 inputscomposite_attributes를 사용하고 results를 생성합니다. 작업의 의미는 decomposition 속성으로 구현됩니다. composite 연산자는 프로그램 시맨틱을 변경하지 않고도 분해로 대체할 수 있습니다. 분해를 인라인 처리해도 동일한 연산 시맨틱을 제공하지 않는 경우 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,
  version = 1 : i32
} : (tensor<f32>, tensor<f32>) -> tensor<f32>

 예시 더보기

이어붙이기

시맨틱

지정된 인수와 동일한 순서로 dimension 차원을 따라 inputs를 연결하고 result 텐서를 생성합니다. 더 공식적으로 result[i0, ..., id, ..., iR-1] = inputs[k][i0, ..., kd, ..., iR-1]이며, 각 항목의 의미는 다음과 같습니다.

  1. id = d0 + ... + dk-1 + kd.
  2. ddimension와 같고 d0, ...은 inputsd번째 크기입니다.

입력

라벨 이름 유형 제약조건
(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
} : (tensor<3x2xi64>, tensor<1x2xi64>) -> tensor<4x2xi64>
// %result: [[1, 2], [3, 4], [5, 6], [7, 8]]

 예시 더보기

상수

시맨틱

상수 value에서 output 텐서를 생성합니다.

입력

라벨 이름 유형 제약조건
(I1) value 상수 (C1)

출력

이름 유형 제약조건
output 텐서 또는 양자화 텐서 (C1)

제약조건

  • (C1) type(value) = type(output).

%output = "stablehlo.constant"() {
  value = dense<[[0.0, 1.0], [2.0, 3.0]]> : tensor<2x2xf32>
} : () -> tensor<2x2xf32>
// %output: [[0.0, 1.0], [2.0, 3.0]]

 예시 더보기

전환

시맨틱

operand 텐서에서 한 요소 유형에서 다른 요소 유형으로 요소별 변환을 실행하고 result 텐서를 생성합니다.

boolean-to-any-supported-type 변환의 경우 값 false은 0으로 변환되고 값 true은 1로 변환됩니다. any-supported-type-to-boolean 변환의 경우 0 값은 false로 변환되고 0이 아닌 값은 true로 변환됩니다. 복잡한 유형에서 이 작업이 어떻게 작동하는지 아래를 참고하세요.

integer-to-integer, integer-to-floating-point 또는 floating-point-to-floating-point 관련 변환의 경우 소스 값이 대상 유형에 정확하게 표시될 수 있는 경우 결과 값은 해당 표현입니다. 그 외의 경우에는 동작은 미정입니다. (#180)

floating-point-to-integer 변환의 경우 소수 부분이 잘립니다. 잘린 값을 대상 유형으로 표현할 수 없는 경우 동작은 미정입니다(#180).

복소수-복소수 변환은 실수와 허수 부분을 변환하는 부동 소수점-부동 소수점 변환과 동일한 동작을 따릅니다.

complex-to-any-other-typeany-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) : (tensor<3xi64>) -> tensor<3xcomplex<f64>>
// %result: [(-1.0, 0.0), (0.0, 0.0), (1.0, 0.0)]

 예시 더보기

컨볼루션

시맨틱

lhs의 창과 rhs의 슬라이스 간의 내적을 계산하고 result를 생성합니다. 다음 다이어그램은 구체적인 예를 사용하여 result의 요소가 lhsrhs에서 계산되는 방식을 보여줍니다.

컨볼루션

좀 더 공식적으로 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 = 1batch_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의 가변 숫자 값 enum (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_count입니다.
    • result_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).
  • 연산이 양자화 텐서를 사용하는 경우:
    • (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).

// %lhs: [[
//        [
//          [1], [2], [5], [6]
//        ],
//        [
//          [3], [4], [7], [8]
//        ],
//        [
//          [10], [11], [14], [15]
//        ],
//        [
//          [12], [13], [16], [17]
//        ]
//      ]]
//
// %rhs: [
//        [[[1]], [[1]], [[1]]],
//        [[[1]], [[1]], [[1]]],
//        [[[1]], [[1]], [[1]]]
//       ]
%result = "stablehlo.convolution"(%lhs, %rhs) {
  window_strides = array<i64: 4, 4>,
  padding = dense<0> : tensor<2x2xi64>,
  lhs_dilation = array<i64: 2, 2>,
  rhs_dilation = array<i64: 1, 1>,
  window_reversal = array<i1: false, false>,
  // In the StableHLO dialect, dimension numbers are encoded via:
  // `[<input dimensions>]x[<kernel dimensions>]->[output dimensions]`.
  // "b" is batch dimension, "f" is feature dimension,
  // "i" is input feature dimension, "o" is output feature dimension,
  // "0/1/etc" are spatial dimensions.
  dimension_numbers = #stablehlo.conv<[b, 0, 1, f]x[0, 1, i, o]->[b, 0, 1, f]>,
  batch_group_count = 1 : i64,
  feature_group_count = 1 : i64,
  precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
//            [[10], [26]],
//            [[46], [62]]
//          ]]

 예시 더보기

코사인

시맨틱

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) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %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) : (tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[64, 63], [56, 0]]

 예시 더보기

custom_call

시맨틱

inputscalled_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 유형의 상수의 가변 개수

출력

이름 유형
results 값의 가변 개수

%results = "stablehlo.custom_call"(%input0) {
  call_target_name = "foo",
  has_side_effect = false,
  backend_config = {bar = 42 : i32},
  api_version = 4 : i32,
  called_computations = [@foo]
} : (tensor<f64>) -> tensor<f64>

나누기

시맨틱

배당금 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) : (tensor<4xf32>, tensor<4xf32>) -> tensor<4xf32>
// %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_configDEFAULT이어야 합니다. DotAlgorithms에는 기본 매개변수가 구현되어 정의되므로 기본값이 없습니다. 따라서 모든 점 알고리즘 필드를 None로 설정하여 빈 점 알고리즘을 지정할 수 있으며, 이 경우 대신 precision_config 값이 사용됩니다.

DotAlgorithm 필드에는 다음이 포함됩니다.

  • lhs_precision_typerhs_precision_type: 작업의 좌항과 우항이 반올림되는 정밀도입니다. 정밀도 유형은 입력과 출력의 저장소 유형과 무관합니다.
  • accumulation_type 누적에 사용되는 정밀도입니다.
  • lhs_component_count, rhs_component_count, num_primitive_operations는 LHS 또는 RHS를 여러 구성요소로 분해하고 이러한 값에 여러 '원시' 내적 연산을 실행하는 알고리즘을 실행할 때 적용됩니다. 일반적으로 더 높은 정밀도를 에뮬레이션하기 위함입니다(예: 더 높은 정밀도 계산을 위해 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의 다양한 enum 수 (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).
  • 작업에서 정규화된 텐서를 사용하는 경우:
    • (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)입니다.
  • !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.

// %lhs: [
//        [[1, 2],
//         [3, 4]],
//        [[5, 6],
//         [7, 8]]
//       ]
// %rhs: [
//        [[1, 0],
//         [0, 1]],
//        [[1, 0],
//         [0, 1]]
//       ]
%result = "stablehlo.dot_general"(%lhs, %rhs) {
  dot_dimension_numbers = #stablehlo.dot<
    lhs_batching_dimensions = [0],
    rhs_batching_dimensions = [0],
    lhs_contracting_dimensions = [2],
    rhs_contracting_dimensions = [1]
  >,
  precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>],
  algorithm = #stablehlo.dot_algorithm<
    lhs_precision_type = tf32,
    rhs_precision_type = tf32,
    accumulation_type = f32,
    lhs_component_count = 1,
    rhs_component_count = 1,
    num_primitive_operations = 1,
    allow_imprecise_accumulation = false
  >
} : (tensor<2x2x2xi64>, tensor<2x2x2xi64>) -> tensor<2x2x2xi64>
// %result: [
//           [[1, 2],
//            [3, 4]],
//           [[5, 6],
//            [7, 8]]
//          ]

 예시 더보기

dynamic_broadcast_in_dim

시맨틱

이 연산은 기능적으로 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_dimensions = array<i64: 2, 1>,
  known_expanding_dimensions = array<i64: 0>,
  known_nonexpanding_dimensions = array<i64: 1>
} : (tensor<1x3xi64>, tensor<3xi64>) -> tensor<2x3x2xi64>
// %result: [
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ],
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ]
//          ]

 예시 더보기

dynamic_conv

시맨틱

이 연산은 컨볼루션 작업과 기능적으로 동일하지만 패딩은 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의 가변 숫자 값 enum (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_count입니다.
    • result_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).
  • 연산이 양자화 텐서를 사용하는 경우:
    • (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).

// %lhs: [[
//        [[1], [2], [5], [6]],
//        [[3], [4], [7], [8]],
//        [[10], [11], [14], [15]],
//        [[12], [13], [16], [17]]
//      ]]
//
// %rhs: [
//         [[[1]], [[1]], [[1]]],
//         [[[1]], [[1]], [[1]]],
//         [[[1]], [[1]], [[1]]]
//        ]
// %padding: [[1, 1],
//            [1, 1]]
%result = "stablehlo.dynamic_conv"(%lhs, %rhs, %padding) {
  window_strides = array<i64: 4, 4>,
  lhs_dilation = array<i64: 2, 2>,
  rhs_dilation = array<i64: 1, 1>,
  window_reversal = array<i1: false, false>,
  dimension_numbers = #stablehlo.conv<raw
    input_batch_dimension = 0,
    input_feature_dimension = 3,
    input_spatial_dimensions = [0, 1],
    kernel_input_feature_dimension = 2,
    kernel_output_feature_dimension = 3,
    kernel_spatial_dimensions = [0, 1],
    output_batch_dimension = 0,
    output_feature_dimension = 3,
    output_spatial_dimensions = [1, 2]
  >,
  feature_group_count = 1 : i64,
  batch_group_count = 1 : i64,
  precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>, tensor<2x2xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
//            [[1], [5]],
//            [[10], [14]]
//          ]]

 예시 더보기

dynamic_gather

시맨틱

이 연산은 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의 크기 크기가 포함되지 않습니다.
    • offset_dim_sizes = shape(slice_sizes)의 경우 collapsed_slice_dims에 해당하는 slice_sizes의 크기 크기가 포함되지 않습니다.
    • combinebatch_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 = #stablehlo.gather<
    offset_dims = [2, 3],
    collapsed_slice_dims = [0],
    start_index_map = [1, 0],
    index_vector_dim = 2>,
  indices_are_sorted = false
} : (tensor<3x4x2xi64>, tensor<2x3x2xi64>, tensor<3xi64>) -> tensor<2x3x2x2xi64>
// %result: [
//            [
//              [[1, 2], [3, 4]],
//              [[3, 4], [5, 6]],
//              [[13, 14], [15, 16]]
//            ],
//            [
//              [[9, 10], [11, 12]],
//              [[11, 12], [13, 14]],
//              [[17, 18], [19, 20]]
//            ]
//          ]

예시 더보기

dynamic_iota

시맨틱

이 연산은 기능적으로 iota op와 동일하지만 결과 셰이프는 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
} : (tensor<2xi64>) -> tensor<4x5xi64>
// %result: [
//           [0, 0, 0, 0, 0],
//           [1, 1, 1, 1, 1],
//           [2, 2, 2, 2, 2],
//           [3, 3, 3, 3, 3]
//          ]

예시 더보기

dynamic_pad

시맨틱

이 작업은 기능적으로 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
) : (tensor<2x3xi64>, tensor<i64>, tensor<2xi64>, tensor<2xi64>, tensor<2xi64>) -> tensor<5x9xi64>
// %result: [
//           [0, 1, 0, 0, 2, 0, 0, 3, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 4, 0, 0, 5, 0, 0, 6, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0]
//          ]

 예시 더보기

dynamic_reshape

시맨틱

이 연산은 기능적으로 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) : (tensor<2x3xi64>, tensor<2xi64>) -> tensor<3x2xi64>
// %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_sizes = array<i64: 2, 2>
} : (tensor<4x4xi32>, tensor<i64>, tensor<i64>) -> tensor<2x2xi32>
// %result: [
//           [1, 1],
//           [1, 1]
//          ]

 예시 더보기

dynamic_update_slice

시맨틱

start_indices에서 시작하는 슬라이스가 update의 값으로 업데이트된다는 점을 제외하고 operand 텐서와 동일한 result 텐서를 생성합니다. 더 엄밀히 말해 result[result_index]는 다음과 같이 정의됩니다.

  • update[update_index]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)
  : (tensor<4x4xi32>, tensor<2x2xi32>, tensor<i64>, tensor<i64>) -> tensor<4x4xi32>
// %result: [
//           [1, 1, 1, 1],
//           [1, 1, 1, 1],
//           [1, 1, 1, 1],
//           [1, 1, 1, 1]
//          ]

예시 더보기

지수

시맨틱

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) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %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) : (tensor<2xf64>) -> tensor<2xf64>
// %result: [0.0, 1.71828187]

예시 더보기

fft

시맨틱

실수 및 복소수 입력/출력에 대한 전방 및 역 푸리에 변환을 실행합니다.

fft_type는 다음 중 하나입니다.

  • FFT: 복소수 대 복소수 FFT를 전달합니다.
  • IFFT: 복소수-복소수의 역 FFT입니다.
  • RFFT: 실수에서 복소수 FFT로 전달합니다.
  • IRFFT: 실수-복소수 역 FFT (즉, 복소수를 취하고 실수를 반환함)

좀 더 공식적으로, 복합 유형의 1차원 텐서를 입력으로 사용하는 fft 함수는 출력과 동일한 유형의 1차원 텐서를 생성하고 이산 푸리에 변환을 계산합니다.

fft_type = FFT의 경우 resultL = 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의 경우 resultfft_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의 경우 resultL = 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의 경우 resultfft_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) operandresult 요소 유형 간의 관계는 다음과 같이 다릅니다.
    • 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) operandresult 중 부동 소수점 유형의 텐서 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 = #stablehlo<fft_type FFT>,
  fft_length = array<i64: 4>
} : (tensor<4xcomplex<f32>>) -> tensor<4xcomplex<f32>>
// %result: [(1.0, 0.0), (1.0, 0.0), (1.0, 0.0), (1.0, 0.0)]

시맨틱

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) : (tensor<5xf32>) -> tensor<5xf32>
// %result: [-1.0, -1.0, 0.0, 0.0, 2.0]

 예시 더보기

수집

시맨틱

start_indices에 지정된 오프셋에서 operand 텐서의 슬라이스를 수집하고 result 텐서를 생성합니다.

다음 다이어그램은 구체적인 예를 사용하여 result의 요소가 operand의 요소에 매핑되는 방식을 보여줍니다. 이 다이어그램은 몇 가지 result 색인 예시를 선택하고 이에 해당하는 operand 색인을 자세히 설명합니다.

gather

더 엄밀히 말해 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]: 여기서 bibatch_index의 개별 요소이며 index_vector_dim < rank(start_indices)인 경우 :index_vector_dim 색인에 삽입됩니다.
    • 그 밖의 경우에는 [start_indices[batch_index]]입니다.
  • axes(operand)d_operand의 경우
    • d_operand = start_index_map[d_start]인 경우 full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand])
    • 그 외에는 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]: 여기서 oioffset_index의 개별 요소이고 0collapsed_slice_dimsoperand_batching_dims의 색인에 삽입됩니다.
  • operand_index = full_start_index + full_batching_index + full_offset_index.

indices_are_sortedtrue이면 구현은 start_indicesstart_index_map를 기준으로 정렬되었다고 가정할 수 있습니다. 그렇지 않으면 동작이 정의되지 않습니다. 더 엄밀히 말해 indices(result)의 모든 i1 < i2에 대해 full_start_index(i1) <= full_start_index(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) 여기서:
    • index_vector_dim에 해당하는 start_indices의 측정기준 크기가 포함되지 않는다는 점을 제외하면 batch_dim_sizes = shape(start_indices)와 같습니다.
    • collapsed_slice_dimsoperand_batching_dims에 해당하는 slice_sizes의 크기는 포함되지 않는 점을 제외하고 offset_dim_sizes = slice_sizes와 같습니다.
    • combinebatch_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 = #stablehlo.gather<
    offset_dims = [3, 4],
    collapsed_slice_dims = [1],
    operand_batching_dims = [0],
    start_indices_batching_dims = [1],
    start_index_map = [2, 1],
    index_vector_dim = 3>,
  slice_sizes = array<i64: 1, 1, 2, 2>,
  indices_are_sorted = false
} : (tensor<2x3x4x2xi32>, tensor<2x2x3x2xi64>) -> tensor<2x2x3x2x2xi32>
// %result: [
//           [
//            [
//             [[1, 2], [3, 4]],
//             [[3, 4], [5, 6]],
//             [[13, 14], [15, 16]]
//            ],
//            [
//             [[33, 34], [35, 36]],
//             [[35, 36], [37, 38]],
//             [[41, 42], [43, 44]]
//            ]
//           ],
//           [
//            [
//             [[1, 2], [3, 4]],
//             [[13, 14], [15, 16]],
//             [[21, 22], [23, 24]]
//            ],
//            [
//             [[43, 44], [45, 46]],
//             [[33, 34], [35, 36]],
//             [[27, 28], [29, 30]]
//            ]
//           ]
//          ]

 예시 더보기

get_dimension_size

시맨틱

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
} : (tensor<2x3xi64>) -> tensor<i32>
// %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))
  index = 0 : i32
} : (tuple<tensor<2xf32>, tuple<tensor<i32>>>) -> tensor<2xf32>
// %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_true_branch) : (tensor<i32>) -> ()
}, {
  "stablehlo.return"(%result_false_branch) : (tensor<i32>) -> ()
}) : (tensor<i1>) -> tensor<i32>
// %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) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %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 = ""
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
// results0#0: [[1, 2], [3, 4]]
%results1:2 = "stablehlo.infeed"(%token) {
  infeed_config = ""
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
// results1#0: [[5, 6], [7, 8]]

예시 더보기

아이오타

시맨틱

output 텐서를 iota_dimension 크기를 따라 0부터 시작하는 오름차순 값으로 채웁니다. 더 공식적으로

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 : i64
} : () -> tensor<4x5xi32>
// %output: [
//           [0, 0, 0, 0, 0],
//           [1, 1, 1, 1, 1],
//           [2, 2, 2, 2, 2],
//           [3, 3, 3, 3, 3]
//          ]

%output = "stablehlo.iota"() {
  iota_dimension = 1 : i64
} : () -> tensor<4x5xi32>
// %output: [
//           [0, 1, 2, 3, 4],
//           [0, 1, 2, 3, 4],
//           [0, 1, 2, 3, 4],
//           [0, 1, 2, 3, 4]
//          ]

 예시 더보기

is_finite

시맨틱

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) : (tensor<7xf64) -> tensor<7xi1>
// %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) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.0, 0.69314718055994529], [1.0986122886681098, 1.3862943611198906]]

 예시 더보기

log_plus_one

시맨틱

operand 텐서에서 요소별로 로그를 더한 1 작업을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.

  • 부동 소수점 수: IEEE-754의 logp1
  • 복소수의 경우: 복소 대수 + 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) : (tensor<5xf64>) -> tensor<5xf64>
// %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) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.5, 0.73105858], [0.88079708, 0.95257413]]

 예시 더보기

지도

시맨틱

dimensions을 따라 computation 매핑 함수를 inputs에 적용하고 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(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = stablehlo.multiply %arg0, %arg1 : tensor<i64>
    stablehlo.return %0 : tensor<i64>
}) {
  dimensions = array<i64: 0, 1>
} : (tensor<2x2xi64>, tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[0, 5], [12, 21]]

 예시 더보기

최대

시맨틱

텐서 lhsrhs에 대해 요소별 최대 연산을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.

  • 불리언: 논리합(OR)
  • 정수의 경우: 정수 최대값
  • 부동 소수점의 경우: 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) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 8]]

 예시 더보기

최소

시맨틱

텐서 lhsrhs에 대해 요소별 최솟값 연산을 실행하고 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) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[1, 2], [3, 4]]

 예시 더보기

곱하기

시맨틱

두 텐서 lhsrhs의 요소별 곱셈을 실행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.

  • 불리언: 논리적 AND.
  • 정수의 경우: 정수 곱셈
  • 부동 소수점의 경우: 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) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 12], [21, 32]]

 예시 더보기

negate

시맨틱

operand 텐서의 요소별 부정을 수행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.

  • 부호 있는 정수의 경우: 정수 부정.
  • 부호 없는 정수의 경우: 부호 있는 정수로 비트 변환, 정수 부정, 부호 없는 정수로 다시 비트 변환
  • 부동 소수점의 경우: 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) : (tensor<2xi32>) -> tensor<2xi32>
// %result: [0, 2]

// Negation operation with with complex tensors
// %operand: (2.5, 0.0)
%result = "stablehlo.negate"(%operand) : (tensor<1xcomplex<f32>>) -> tensor<1xcomplex<f32>>
// %result: [-2.5, -0.0]

 예시 더보기

않는

시맨틱

텐서 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) : (tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[-2, -3], [-4, -5]]

// Bitwise operation with with boolean tensors
// %operand: [true, false]
%result = "stablehlo.not"(%operand) : (tensor<2xi1>) -> tensor<2xi1>
// %result: [false, true]

 예시 더보기

optimization_barrier

시맨틱

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) : (tensor<f32>, tensor<f32>) -> (tensor<f32>, tensor<f32>)
// %result0: 0.0
// %result1: 1.0

 예시 더보기

또는

시맨틱

두 텐서 lhsrhs의 요소별 OR을 수행하고 result 텐서를 생성합니다. 요소 유형에 따라 다음을 실행합니다.

  • 불리언: 논리합(OR)
  • 정수의 경우: 비트 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) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 12]]

// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, true]]

 예시 더보기

outfeed

시맨틱

inputs를 아웃피드에 쓰고 result 토큰을 생성합니다.

outfeed_config의 시맨틱은 구현에 따라 정의됩니다.

입력

라벨 이름 유형
(I1) inputs 텐서 또는 양자화 텐서의 가변 개수
(I2) token token
(I3) outfeed_config string 유형의 상수

출력

이름 유형
result token

%result = "stablehlo.outfeed"(%input0, %token) {
  outfeed_config = ""
} : (tensor<2x2x2xi64>, !stablehlo.token) -> !stablehlo.token

 예시 더보기

패드

시맨틱

주어진 padding_value로 텐서 주변과 텐서 요소 간에 패딩을 추가하여 operand를 확장합니다.

edge_padding_lowedge_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_low = array<i64: 0, 1>,
  edge_padding_high = array<i64: 2, 1>,
  interior_padding = array<i64: 1, 2>
} : (tensor<2x3xi32>, tensor<i32>) -> tensor<5x9xi32>
// %result: [
//           [0, 1, 0, 0, 2, 0, 0, 3, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 4, 0, 0, 5, 0, 0, 6, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0]
//          ]

 예시 더보기

partition_id

시맨틱

현재 프로세스의 partition_id을 생성합니다.

출력

이름 유형
result ui32 유형의 0차원 텐서

%result = "stablehlo.partition_id"() : () -> tensor<ui32>

예시 더보기

popcnt

시맨틱

operand 텐서에 설정된 비트 수의 요소별 카운트를 수행하고 result 텐서를 생성합니다.

입력

라벨 이름 유형 제약조건
(I1) operand 정수 유형의 텐서 (C1)

출력

이름 유형 제약조건
result 정수 유형의 텐서 (C1)

제약조건

  • (C1) type(operand) = type(result).

// %operand: [0, 1, 2, 127]
%result = "stablehlo.popcnt"(%operand) : (tensor<4xi64>) -> tensor<4xi64>
// %result: [0, 1, 1, 7]

 예시 더보기

전력

시맨틱

lhs 텐서를 rhs 텐서로 요소별로 거듭제곱하고 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) : (tensor<6xf64>, tensor<6xf64>) -> tensor<6xf64>
// %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) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [1.0, 3.0]

 예시 더보기

recv

시맨틱

channel_id를 사용하여 채널에서 데이터를 수신하고 results를 생성합니다.

is_host_transfertrue이면 작업은 호스트에서 데이터를 전송합니다. 그렇지 않으면 다른 기기에서 데이터를 전송합니다. 이는 구현에 따라 달라집니다. 이 플래그는 channel_type에 제공된 정보를 중복하므로 향후 둘 중 하나만 유지할 계획입니다(#666).

results는 먼저 오는 페이로드 값과 마지막 토큰으로 구성됩니다. 향후 명확성을 개선하기 위해 페이로드와 토큰을 두 개의 별도 출력으로 분할할 계획입니다(#670).

입력

라벨 이름 유형 제약조건
(I1) token token (C4)
(I2) channel_id si64 유형의 상수
(I3) channel_type DEVICE_TO_DEVICEHOST_TO_DEVICE의 enum (C1)
(I4) is_host_transfer i1 유형의 상수 (C1)

출력

이름 유형 제약조건
results 텐서, 양자화 텐서 또는 토큰의 가변 개수 (C2-C4)

제약조건

  • (C1) channel_type는 다음과 같이 정의됩니다.
    • is_host_transfer = true인 경우 HOST_TO_DEVICE
    • 그 밖의 경우에는 DEVICE_TO_DEVICE입니다.
  • (C2) 0 < size(results).
  • (C3) is_empty(result[:-1]) 또는 is_tensor(type(results[:-1])).
  • (C4) is_token(type(results[-1])).

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

예시 더보기

reduce

시맨틱

dimensions을 따라 inputsinit_values에 축소 함수 body를 적용하고 results 텐서를 생성합니다.

축소 순서는 구현에 따라 정의됩니다. 즉, 작업이 모든 구현의 모든 입력에 대해 동일한 결과를 생성하도록 보장하려면 bodyinit_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_space(input_slices_converted)의 모든 index에 대한 input_slices_converted...[index] 값(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(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  dimensions = array<i64: 1>
} : (tensor<1x6xi64>, tensor<i64>) -> tensor<1xi64>
// %result = [15]

 예시 더보기

reduce_precision

시맨틱

operandexponent_bitsmantissa_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
} : (tensor<6xf64>) -> tensor<6xf64>
// Logical values: +Inf, NaN, 0.0, 0.0, 65504.0, +Inf
// %output: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0.0, 0.0, 65504.0, 0x7FF0000000000000]

예시 더보기

reduce_scatter

시맨틱

reduce_scatter

StableHLO 프로세스 그리드의 각 프로세스 그룹 내에서 각 프로세스의 operand 텐서 값에 대해 computations를 사용하여 감소를 실행하고, 감소 결과를 scatter_dimension를 따라 부분으로 분할하고, 분할된 부분을 프로세스 간에 흩어 result를 생성합니다.

이 작업은 StableHLO 프로세스 그리드를 다음과 같이 정의된 process_groups로 분할합니다.

  • channel_id <= 0 and use_global_device_ids = false인 경우 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 = true인 경우 flattened_ids(replica_groups)입니다.

그런 다음 각 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_replicas
    • cross_replica_and_partition가 사용된 경우 num_replicas
    • flattened_ids가 사용된 경우 num_processes
  • (C5) 0 <= replica_groups < size(replica_groups).
  • (C6) use_global_device_ids = true인 경우 channel_id > 0
  • (C7) computationis_promotable(element_type(operand), E)인 경우 (tensor<E>, tensor<E>) -> (tensor<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(%arg0: tensor<i64>, %arg1: tensor<i64>):
  %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
  "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  scatter_dimension = 1 : i64,
  replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor<2x4xi64>) -> tensor<2x2xi64>
//
// %result@(0, 0): [[10, 12],
//                  [18, 20]]
// %result@(1, 0): [[14, 16],
//                  [22, 24]]

 예시 더보기

reduce_window

시맨틱

축소 함수 bodyinputsinit_values의 창에 적용하고 results를 생성합니다.

다음 다이어그램은 구체적인 예를 사용하여 results...의 요소가 inputs...에서 계산되는 방식을 보여줍니다.

reduce_window

더 엄밀히 말해 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(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  window_dimensions = array<i64: 2, 1>,
  window_strides = array<i64: 4, 1>,
  base_dilations = array<i64: 2, 1>,
  window_dilations = array<i64: 3, 1>,
  padding = dense<[[2, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<3x2xi64>, tensor<i64>) -> tensor<2x2xi64>
// %result = [[0, 0], [3, 4]]

예시 더보기

나머지

시맨틱

배수 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)).

부동 소수점 요소 유형의 경우 이 연산은 IEEE-754 사양의 remainder 연산과 대조됩니다. 여기서 d은 짝수와의 동률을 고려하여 lhs/rhs의 정확한 값에 가장 근접한 정수 값입니다.

입력

라벨 이름 유형 제약조건
(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) : (tensor<4xi64>, tensor<4xi64>) -> tensor<4xi64>
// %result: [2, -2, 2, -2]

 예시 더보기

replica_id

시맨틱

현재 프로세스의 replica_id을 생성합니다.

출력

이름 유형
result ui32 유형의 0차원 텐서

%result = "stablehlo.replica_id"() : () -> tensor<ui32>

 예시 더보기

형태를 변경하다

시맨틱

operand 텐서를 result 텐서로 리셰이프합니다. 개념적으로는 동일한 표준 표현을 유지하지만 모양을 변경할 수 있습니다(예: tensor<2x3xf32>에서 tensor<3x2xf32> 또는 tensor<6xf32>로).

더 엄밀히 말해 result[result_index] = operand[operand_index]result_indexoperand_indexindex_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) : (tensor<2x3xi32>) -> tensor<3x2xi32>
// %result: [[1, 2], [3, 4], [5, 6]]

예시 더보기

reverse

시맨틱

지정된 dimensions를 따라 operand의 요소 순서를 반대로 하고 result 텐서를 생성합니다. 더 엄밀히 말하면 result[result_index] = operand[operand_index]이며 여기서

  • dimensionsd가 있는 경우 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) {
  dimensions = array<i64: 1>
} : (tensor<3x2xi32>) -> tensor<3x2xi32>
// %result: [[2, 1], [4, 3], [6, 5]]

예시 더보기

RG

시맨틱

rng_distribution 알고리즘을 사용하여 랜덤 숫자를 생성하고 지정된 형식 shaperesult 텐서를 생성합니다.

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 UNIFORMNORMAL의 enum (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 = #stablehlo<rng_distribution UNIFORM>
} : (tensor<i32>, tensor<i32>, tensor<2xi64>) -> tensor<3x3xi32>
// %result: [
//           [1, 0, 1],
//           [1, 1, 1],
//           [0, 0, 0]
//          ]

rng_bit_generator

시맨틱

초기 상태 initial_state가 지정된 경우 의사 난수 생성기 알고리즘 rng_algorithm를 사용하여 균일한 임의 비트로 채워진 output와 업데이트된 출력 상태 output_state를 반환합니다. 출력은 initial_state의 확정 함수로 보장되지만 구현 간에 확정적인 것은 아닙니다.

rng_algorithm는 다음 중 하나입니다.

  • DEFAULT: 구현 정의 알고리즘입니다.
  • THREE_FRY: Threefry 알고리즘의 구현 정의 변형입니다.*
  • PHILOX: Philox 알고리즘의 구현 정의 변형입니다.*

* 참고: Salmon et al. SC 2011. 병렬 난수: 쉽고 간단합니다.

입력

라벨 이름 유형 제약조건
(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인 경우 2
    • rng_algorithm = PHILOX인 경우 2 또는 3입니다.

// %initial_state: [1, 2]
%output_state, %output = "stablehlo.rng_bit_generator"(%initial_state) {
  rng_algorithm = #stablehlo<rng_algorithm THREE_FRY>
} : (tensor<2xui64>) -> (tensor<2xui64>, tensor<2x2xui64>)
// %output_state: [1, 6]
// %output: [
//           [9236835810183407956, 16087790271692313299],
//           [18212823393184779219, 2658481902456610144]
//          ]

round_nearest_afz

시맨틱

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) : (tensor<5xf64>) -> tensor<5xf64>
// %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) : (tensor<5xf64>) -> tensor<5xf64>
// %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) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %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]: 여기서 siupdate_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]: 여기서 wiupdate_window_index의 개별 요소이고 0inserted_window_dimsinput_batching_dims의 색인에 삽입됩니다.
  • result_index = full_start_index + full_batching_index + full_window_index.

이 경우 results = exec(schedule, inputs), 각 항목의 의미는 다음과 같습니다.

  • scheduleindex_space(updates[0])의 구현 정의 순열입니다.
  • exec([update_index, ...], results) = exec([...], updated_results) 각 항목의 의미는 다음과 같습니다.
    • result_indexshape(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_resultsresults...[result_index]updated_values...로 설정된 results의 사본입니다.
    • 그렇지 않은 경우 다음 단계를 따릅니다.
    • updated_results = results.
  • exec([], results) = results.

indices_are_sortedtrue이면 구현은 scatter_indicesscatter_dims_to_operand_dims를 기준으로 정렬되었다고 가정할 수 있습니다. 그렇지 않으면 동작이 정의되지 않습니다. 더 엄밀히 말해 indices(result)의 모든 i1 < i2에 대해 full_start_index(i1) <= full_start_index(i2)입니다.

unique_indicestrue이면 구현은 분산되는 모든 result_index 색인이 고유하다고 가정할 수 있습니다. unique_indicestrue이지만 흩어지는 색인이 고유하지 않은 경우 동작은 정의되지 않습니다.

입력

라벨 이름 유형 제약조건
(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)와 같습니다.
    • inserted_window_dimsinput_batching_dims에 해당하는 inputs[0]의 크기 측정기준은 포함되지 않는 점을 제외하고 update_window_dim_sizes <= shape(inputs[0])과 같습니다.
    • combineupdate_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]]
//           ]
//          ]
%result = "stablehlo.scatter"(%input, %scatter_indices, %update) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  scatter_dimension_numbers = #stablehlo.scatter<
    update_window_dims = [3, 4],
    inserted_window_dims = [1],
    input_batching_dims = [0],
    scatter_indices_batching_dims = [1],
    scatter_dims_to_operand_dims = [2, 1],
    index_vector_dim = 3>,
  indices_are_sorted = false,
  unique_indices = false
} : (tensor<2x3x4x2xi64>, tensor<2x2x3x2xi64>, tensor<2x2x3x2x2xi64>) -> tensor<2x3x4x2xi64>
// %result: [
//           [
//            [[3, 4], [6, 7], [6, 7], [7, 8]],
//            [[9, 10],[11, 12], [15, 16], [17, 18]],
//            [[17, 18], [19, 20], [22, 23], [24, 25]]
//           ],
//           [
//            [[25, 26], [28, 29], [30, 31], [31, 32]],
//            [[35, 36], [38, 39], [38, 39], [39, 40]],
//            [[41, 42], [44, 45], [46, 47], [47, 48]]
//           ]
//          ]

 예시 더보기

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) : (tensor<2x2xi1>, tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 2], [3, 8]]

예시 더보기

select_and_scatter

시맨틱

select를 사용하여 input 텐서의 reduce_window 결과에 따라 scatter를 사용하여 source 텐서의 값을 흩어 뿌리고 result 텐서를 생성합니다.

다음 다이어그램은 구체적인 예를 사용하여 result의 요소가 operandsource에서 계산되는 방식을 보여줍니다.

select_and_scatter

더 격식 있게 작성하는 방법은 다음과 같습니다.

  • 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는 기본 reduceschedule (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_indexoperand 요소가 있는 경우 selected_index(source_index) = operand_index입니다.
    • source_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(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.compare"(%arg0, %arg1) {
      comparison_direction = #stablehlo<comparison_direction GE>
    } : (tensor<i64>, tensor<i64>) -> tensor<i1>
    "stablehlo.return"(%0) : (tensor<i1>) -> ()
}, {
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  window_dimensions = array<i64: 3, 1>,
  window_strides = array<i64: 2, 1>,
  padding = dense<[[0, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<4x2xi64>, tensor<2x2xi64>, tensor<i64>) -> tensor<4x2xi64>
// %result: [[0, 0], [0, 0], [5, 14], [7, 0]]

 예시 더보기

보내기

시맨틱

inputs를 채널 channel_id에 전송하고 result 토큰을 생성합니다.

is_host_transfertrue이면 작업은 데이터를 호스트로 전송합니다. 그렇지 않으면 데이터를 다른 기기로 전송합니다. 이는 구현에 따라 달라집니다. 이 플래그는 channel_type에 제공된 정보를 중복하므로 향후 둘 중 하나만 유지할 계획입니다(#666).

입력

라벨 이름 유형 제약조건
(I1) inputs 텐서 또는 양자화 텐서의 가변 개수
(I2) token token
(I3) channel_id si64 유형의 상수
(I4) channel_type DEVICE_TO_DEVICEDEVICE_TO_HOST의 enum (C1)
(I5) is_host_transfer i1 유형의 상수 (C1)

출력

이름 유형
result token

제약조건

  • (C1) channel_type는 다음과 같이 정의됩니다.
    • is_host_transfer = true인 경우 DEVICE_TO_HOST
    • 그 밖의 경우에는 DEVICE_TO_DEVICE입니다.

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

 예시 더보기

shift_left

시맨틱

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): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %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): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %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): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %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) : (tensor<5xf64>) -> tensor<5xf64>
// Logical values: +NaN, -1.0, -0.0, +0.0, 1.0
// %result: [0x7FFFFFFFFFFFFFFF, -1.0, -0.0, 0.0, 1.0]

 예시 더보기

사인

시맨틱

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) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %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_indices = array<i64: 1, 2>,
  limit_indices = array<i64: 3, 4>,
  strides = array<i64: 1, 1>
} : (tensor<3x4xi64>) -> tensor<2x2xi64>
// % result: [
//            [1, 1],
//            [1, 1]
//           ]

예시 더보기

정렬

시맨틱

comparator에 따라 dimension 측정기준을 따라 inputs의 1차원 슬라이스를 함께 정렬하고 results을 생성합니다.

다른 작업의 유사한 입력과 달리 dimension는 아래에 설명된 시맨틱스와 함께 음수 값을 허용합니다. 향후 일관성 문제로 인해 이러한 작업이 허용되지 않을 수 있습니다(#1377).

is_stable이 true이면 정렬이 안정적입니다. 즉, 비교 연산자가 동일한 것으로 간주하는 요소의 상대적 순서가 유지됩니다. 입력이 하나인 경우 두 요소 e1e2comparator(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]: 여기서 riNresult_index의 개별 요소이고 :adjusted_dimension에 삽입됩니다.
  • inputs_together = (inputs[0]..., ..., inputs[N-1]...).
  • results_together[result_slice] = sort(inputs_together[result_slice], comparator_together).
  • 여기서 sort는 1차원 슬라이스를 내림차순이 아닌 순서로 정렬하며, 왼쪽 인수가 오른쪽 두 번째 인수보다 작으면 comparator_togethertrue를 반환할 것으로 예상합니다.
  • 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(%arg0: tensor<i64>, %arg1: tensor<i64>, %arg2: tensor<i64>, %arg3: tensor<i64>):
    %predicate = "stablehlo.compare"(%arg0, %arg1) {
      comparison_direction = #stablehlo<comparison_direction GT>
    } : (tensor<i64>, tensor<i64>) -> tensor<i1>
    "stablehlo.return"(%predicate) : (tensor<i1>) -> ()
}) {
  dimension = 0 : i64,
  is_stable = true
} : (tensor<2x3xi64>, tensor<2x3xi64>) -> (tensor<2x3xi64>, tensor<2x3xi64>)
// %result0 = [[3, 2, 3], [1, 2, 1]]
// %result1 = [[1, 2, 1], [3, 2, 3]]

예시 더보기

sqrt

시맨틱

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) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [2.0, 3.0]]

 예시 더보기

subtract

시맨틱

두 텐서 lhsrhs의 요소별 차기를 실행하고 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) : (tensor<2x2xf32>, tensor<2x2xf32>) -> (tensor<2x2xf32>)
// %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) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %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) : (tensor<3xf32>) -> tensor<3xf32>
// %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) permutationrange(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) {
  permutation = array<i64: 2, 1, 0>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>
// %result: [
//           [[1,7], [3,9], [5,11]],
//           [[2,8], [4,10], [6,12]]
//          ]

 예시 더보기

triangular_solve

시맨틱

하위 또는 상위 삼각형 계수 행렬을 사용하여 연립일차방정식 일괄 풀이

더 공식적으로 ab가 주어지면 result[i0, ..., iR-3, :, :]left_sidetrue일 때 또는 left_side일 때 x * op(a[i0, ..., iR-3, :, :]) = b[i0, ..., iR-3, :, :]op(a[i0, ..., iR-3, :, :]) * x = b[i0, ..., iR-3, :, :]에 대한 해법으로, op(a)transpose_a에 의해 결정되는 변수 x(다음 중 하나일 수 있음)를 해결합니다.false

  • NO_TRANSPOSE: a를 있는 그대로 사용하여 작업을 실행합니다.
  • TRANSPOSE: a의 행과 열을 바꿔 작업을 실행합니다.
  • ADJOINT: a의켤레 전치에서 작업을 실행합니다.

입력 데이터는 lowertrue인 경우 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의 열거형

출력

이름 유형 제약조건
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 = #stablehlo<transpose NO_TRANSPOSE>
} : (tensor<3x3xf32>, tensor<3x3xf32>) -> tensor<3x3xf32>
// %result: [
//           [2.0, 0.0, 0.0],
//           [0.0, 2.0, 0.0],
//           [0.0, 0.0, 2.0]
//          ]

tuple

시맨틱

val 값에서 result 튜플을 생성합니다.

입력

라벨 이름 유형 제약조건
(I1) val 값의 가변 개수 (C1)

출력

이름 유형 제약조건
result tuple (C1)

제약조건

  • (C1) resultEi = type(val[i])인 경우 tuple<E0, ..., EN-1> 유형입니다.

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

 예시 더보기

uniform_dequantize

시맨틱

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) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2xf32>
// %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) : (tensor<2xf32>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>
// %result: [10, 10]

// %operand: [10, 10]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-20,0.2:-30}>>
// %result: [20, 45]

동안

시맨틱

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(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %cond = "stablehlo.compare"(%arg0, %ten) {
      comparison_direction = #stablehlo<comparison_direction LT>
    } : (tensor<i64>, tensor<i64>) -> tensor<i1>
    stablehlo.return %cond : tensor<i1>
  }, {
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %new_sum = stablehlo.add %arg1, %one : tensor<i64>
    %new_i = stablehlo.add %arg0, %one : tensor<i64>
    stablehlo.return %new_i, %new_sum : tensor<i64>, tensor<i64>
}) : (tensor<i64>, tensor<i64>) -> (tensor<i64>, tensor<i64>)
// %results0: 10
// %results1: 10

예시 더보기

xor

시맨틱

두 텐서 lhsrhs의 요소별 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) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[4, 4], [4, 12]]

// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.xor"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, false]]

예시 더보기

언어 상호 운용성

현재 실제 StableHLO 프로그램에는 StableHLO에서 정의하지 않은 작업이 포함되어 있는 경우가 있습니다.

모듈, 함수, 호출, 반환

StableHLO는 ModuleOp, FuncOp, CallOp, ReturnOp에 업스트림 MLIR 작업을 사용합니다. 이는 FuncOp 및 ModuleOp을 타겟팅하는 많은 유용한 패스가 작성되고 많은 컴파일 파이프라인에서 이러한 작업이 있을 것으로 예상되므로 기존 MLIR 메커니즘과의 상호 운용성을 개선하기 위한 조치입니다. 이러한 작업에는 완전한 호환성 보장이 적용됩니다. 이러한 작업에 대해 호환되지 않는 방식 (즉, 삭제)이 변경되면 호환성을 유지하기 위해 StableHLO에 상응하는 항목이 추가됩니다.

CHLO

CHLO opset에는 StableHLO로 분해되는 상위 수준 작업이 포함됩니다. 현재 CHLO에 대한 호환성 보장은 없습니다. 호환성을 보장하려면 직렬화 전에 chlo-legalize-to-stablehlo 패스를 사용해야 합니다.

도형 작업

동적 StableHLO 프로그램에서 핵심 MLIR 방언의 특정 작업을 사용하여 도형 계산을 실행하는 것은 커뮤니티에서 일반적인 사용 사례입니다. 이러한 작업에는 shape 언어 작업(예: shape_of 또는 num_elements), tensor 언어(예: dim 또는 from_elements), 기본 제공 index 유형이 포함됩니다.

동적 RFC > O2에서는 이를 지원 범위 외로 지정하지만 상호 운용성 목적으로 index 유형에 대한 일부 지원이 포함되어 있습니다. 이러한 연산이나 유형에 대한 호환성 보장은 없습니다. shape-legalize-to-stablehlo 패스를 사용하여 이러한 작업을 완전히 지원되는 StableHLO 작업으로 변환할 수 있습니다.

지원 중단된 작업

MHLO에서 상속된 여러 StableHLO 작업이 있으며 이 작업은 지원 중단되어 StableHLO에서 지원이 중단될 예정입니다. 이러한 삭제에 관한 자세한 내용은 StableHLO v1.0 정리 #2283에서 확인할 수 있습니다. 이러한 지원 중단에 관한 추적기 문제는 #2340입니다.

이러한 작업은 다음과 같은 몇 가지 카테고리로 분류됩니다.

  • StableHLO 연산의 'Not in 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, convolution 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). 커뮤니티 의견을 기다리는 동안 이러한 연산자는 삭제되거나 전체 지원과 함께 사양에 추가됩니다. 이러한 작업 future가 알려질 때까지는 6개월 동안의 호환성만 보장됩니다.

실행

순차적 실행

StableHLO 프로그램은 main 함수에 입력 값을 제공하고 출력 값을 계산하여 실행됩니다. 함수의 출력 값은 해당 return 작업에 루팅된 작업 그래프를 실행하여 계산됩니다.

실행 순서는 데이터 흐름과 일치하는 한(즉, 작업이 사용 전에 실행되는 경우) 구현에서 정의됩니다. StableHLO에서 모든 부작용 작업은 하나의 토큰을 사용하고 하나의 토큰을 생성합니다(여러 토큰을 after_all를 통해 하나의 토큰으로 다중화할 수 있음). 따라서 부작용의 실행 순서도 데이터 흐름과 일치합니다. 예를 들어 아래 프로그램에는 두 가지 실행 순서(%0%1%2return%1%0%2return)가 있습니다.

func.func @main() -> tensor<f64> {
  %0 = stablehlo.constant dense<1.0> : tensor<f64>
  %1 = stablehlo.constant dense<2.0> : tensor<f64>
  %2 = stablehlo.add %0, %1 : tensor<f64>
  return %2 : tensor<f64>
}

더 엄밀히 말하자면 StableHLO 프로세스는 1) StableHLO 프로그램, 2) 작업 상태(아직 실행되지 않음, 이미 실행됨), 3) 프로세스가 작업 중인 중간 값의 조합입니다. 프로세스는 main 함수의 입력 값으로 시작하고, 작업 상태와 중간 값을 업데이트하는 작업 그래프를 통해 진행되며, 출력 값으로 끝납니다. 추가 형식화는 미정입니다(#484).

동시 실행

StableHLO 프로그램은 동시에 실행할 수 있으며, 모두 ui32 유형인 num_replicasnum_partitions의 2D 프로세스 그리드로 구성됩니다.

StableHLO 프로세스 그리드에서 StableHLO 프로세스 num_replicas * num_partitions개가 동시에 실행되고 있습니다. 각 프로세스에는 고유한 process_id = (replica_id, partition_id)가 있습니다. 여기서 replica_ids = range(num_replicas)replica_idpartition_ids = range(num_partitions)partition_id는 모두 ui32 유형입니다.

프로세스 그리드의 크기는 모든 프로그램에 대해 정적으로 알려져 있습니다(향후 StableHLO 프로그램 #650의 명시적 부분으로 만들 계획임). 프로세스 그리드 내의 위치는 모든 프로세스에 대해 정적으로 알려져 있습니다. 각 프로세스는 replica_idpartition_id 연산을 통해 프로세스 그리드 내 위치에 액세스할 수 있습니다.

프로세스 그리드 내에서 프로그램은 모두 동일할 수 있고('단일 프로그램, 여러 데이터' 스타일), 모두 다를 수 있고('여러 프로그램, 여러 데이터' 스타일), 그 중간일 수 있습니다. 향후 GSPMD(#619)를 비롯한 병렬 StableHLO 프로그램을 정의하는 다른 문법에 대한 지원을 도입할 계획입니다.

프로세스 그리드 내에서 프로세스는 대부분 서로 독립적입니다. 프로세스마다 별도의 작업 상태, 별도의 입력/중간/출력 값이 있으며, 아래에 설명된 소수의 집합적 작업을 제외하고 대부분의 작업은 프로세스 간에 별도로 실행됩니다.

대부분의 작업 실행은 동일한 프로세스의 값만 사용하므로 일반적으로 이러한 값을 이름으로 참조하는 것이 명확합니다. 하지만 집합 작업의 시맨틱스를 설명할 때는 이것만으로는 충분하지 않으며, 특정 프로세스 내에서 name 값을 참조하도록 name@process_id 표기법을 일으킵니다. (이러한 관점에서 정규화되지 않은 namename@(replica_id(), partition_id())의 약칭으로 볼 수 있습니다.)

아래에 설명된 대로 포인트 투 포인트 통신 및 집합 연산으로 도입된 동기화를 제외하고 프로세스 전반의 실행 순서는 구현에서 정의됩니다.

포인트 투 포인트 통신

StableHLO 프로세스는 StableHLO 채널을 통해 서로 통신할 수 있습니다. 채널은 si64 유형의 양수 ID로 표현됩니다. 다양한 작업을 통해 채널로 값을 전송하고 채널에서 수신할 수 있습니다.

이러한 채널 ID의 출처, 프로세스 프로그램이 이를 인식하는 방법, 이러한 채널 ID로 인해 도입되는 동기화 유형 등 추가 형식화는 미정입니다(#484).

스트리밍 통신

모든 StableHLO 프로세스는 두 가지 스트리밍 인터페이스에 액세스할 수 있습니다.

  • 읽을 수 있는 Infeed
  • 쓸 수 있는 아웃피드입니다.

프로세스 간 통신에 사용되어 양측에 프로세스가 있는 채널과 달리 인피드 및 아웃피드에는 다른 끝 구현이 정의되어 있습니다.

스트리밍 통신이 실행 순서에 미치는 영향과 스트리밍 통신으로 인해 도입되는 동기화 유형과 같은 추가 형식화는 미정입니다(#484).

집단 작업

StableHLO에는 all_gather, all_reduce, all_to_all, collective_broadcast, collective_permute, reduce_scatter의 6가지 집합 연산이 있습니다. 이러한 모든 연산은 StableHLO 프로세스 그리드의 프로세스를 StableHLO 프로세스 그룹으로 분할하고 각 프로세스 그룹 내에서 다른 프로세스 그룹과 독립적으로 공동 계산을 실행합니다.

각 프로세스 그룹 내에서 집합 운영팀은 동기화 배리어를 도입할 수 있습니다. 동기화가 정확히 언제 발생하는지, 프로세스가 이 장벽에 도달하는 정확한 방법, 도달하지 못하면 어떻게 되는지 등에 관한 추가 공식화는 미정입니다(#484).

프로세스 그룹에 교차 파티션 통신이 포함된 경우(즉, 프로세스 그룹에 파티션 ID가 다른 프로세스가 있는 경우) 집합 연산을 실행하려면 채널이 필요하며 집합 연산은 si64 유형의 양수 channel_id를 제공해야 합니다. 교차 복제본 통신에는 채널이 필요하지 않습니다.

집합 연산에서 실행하는 계산은 개별 연산에 따라 다르며 위의 개별 연산 섹션에 설명되어 있습니다. 그러나 프로세스 그리드를 프로세스 그룹으로 분할하는 전략은 이러한 작업 간에 공유되며 이 섹션에 설명되어 있습니다. 더 정확하게는 StableHLO는 다음 4가지 전략을 지원합니다.

cross_replica

각 프로세스 그룹 내에서 복제본 간 통신만 발생합니다. 이 전략은 복제본 ID 목록 목록인 replica_groups를 사용하고 replica_groupspartition_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

각 프로세스 그룹 내에서만 교차 파티션 통신이 발생합니다. 이 전략은 파티션 ID 목록 목록인 partition_groups를 사용하고 partition_groupsreplica_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

복제본 간 통신과 파티션 간 통신은 모두 각 프로세스 그룹 내에서 발생할 수 있습니다. 이 전략은 복제본 ID 목록 목록인 replica_groups를 사용하고 partition_ids를 사용하여 각 replica_group의 데카르트 곱을 계산합니다. 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, 위키피디아)을 사용합니다. 두 가지 사항이 수정되었습니다. 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는 텐서이지만 0rank(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) {
  permutation = dense<[2, 1, 0]> : tensor<3xi64>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>

이 연산에서 permutation는 상수이므로 시맨틱과 제약 조건 모두에서 Value로 사용할 수 있습니다. 반면 operandresult는 시맨틱스에서는 Value로 사용할 수 있지만 제약조건에서는 Placeholder로만 사용할 수 있습니다.

함수

유형 구성

유형을 생성하는 데 사용할 수 있는 함수가 없습니다. 대신 일반적으로 더 간결한 유형 문법을 직접 사용합니다. 예를 들어 function_type( [tensor_type([], E), tensor_type([], E)], [tensor_type([], E)]) 대신 (tensor<E>, tensor<E>) -> (tensor<E>)을 사용합니다.

유형에 관한 함수

  • element_type는 텐서 유형과 양자화 텐서 유형에 대해 정의되며 상응하는 TensorType 또는 QuantizedTensorTypeTensorElementType 또는 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) -> Valueis_quantized(x) and quantization_dimension(x) is not None의 바로가기입니다.

  • is_per_tensor_quantized(x: Value | Placeholder | Type) -> Valueis_quantized(x) and quantization_dimension(x) is None의 줄임말입니다.

  • is_promotable(x: Type, y: Type) -> boolx 유형을 y 유형으로 승격할 수 있는지 확인합니다. xyQuantizedTensorElementType인 경우 프로모션은 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) -> Valueis_quantized_tensor_element_type(x)의 바로가기입니다.

  • is_type_name(x: Value | Placeholder | Type) -> Value. 모든 유형에 사용할 수 있습니다. 예를 들어 xFloatType이면 is_float(x)true를 반환합니다. x가 값 또는 자리표시자인 경우 이 함수는 is_type_name(type(x))의 바로가기입니다.

  • max_value(x: Type) -> ValueTensorElementType의 최대값을 반환합니다. xTensorElementType이 아니면 None를 반환합니다.

  • min_value(x: Type) -> ValueTensorElementType의 가능한 최솟값을 반환합니다. xTensorElementType이 아니면 None를 반환합니다.

  • member_name(x: Value | Placeholder | Type) -> Any. 모든 유형의 모든 구성원 정의 member_name에 사용할 수 있습니다. 예를 들어 tensor_element_type(x)는 상응하는 TensorTypeTensorElementType 부분을 반환합니다. x가 값 또는 자리표시자이면 이 함수는 member_name(type(x))의 바로가기입니다. x가 적절한 멤버가 있는 유형이 아니거나 이러한 유형의 값 또는 자리표시자가 아닌 경우 None를 반환합니다.

  • is_empty_algorithm(*args: Type)는 모든 점 알고리즘 필드가 None로 설정되어 있는지 확인합니다. 점 알고리즘에는 구현 정의 기본 동작이 있으므로 기본값을 지정하면 잘못됩니다.

값 구성

  • operation_name(*xs: Value | Type) -> Value. 모든 작업에 사용할 수 있습니다. 예를 들어 add(lhs, rhs)는 두 개의 텐서 값 lhsrhs을 사용하고 이러한 입력으로 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)는 상응하는 ComplexConstantRealPart 부분을 반환합니다. x가 적절한 구성원이 있는 값이 아니면 None를 반환합니다.

  • same(x: Value) -> Value는 텐서에 정의되며 x의 요소가 모두 같으면 true를 반환하고 그렇지 않으면 false를 반환합니다. 텐서에 요소가 없으면 '모두 같음'으로 계산됩니다. 즉, 함수는 true을 반환합니다. x가 텐서가 아니면 None을 반환합니다.

  • split(x: Value, num_results: Value, axis: Value) -> Value는 텐서에 정의되며 축 axis을 따라 xnum_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) -> Valuerange(rank(x))의 단축키입니다.

  • dim(x: Value | Placeholder | Type, axis: Value) -> Valueshape(x)[axis]의 바로가기입니다.

  • dims(x: Value | Placeholder | Type, axes: List) -> Listlist(map(lambda axis: dim(x, axis), axes))의 단축키입니다.

  • index_space(x: Value | Placeholder | Type) -> Value는 텐서에 정의되며 오름차순 사전순으로 정렬된 해당 TensorTypesize(x) 색인(예: [0, ..., 0], [0, ..., 1], ..., shape(x) - 1)을 반환합니다. x가 텐서 유형, 정량화된 텐서 유형, 이러한 유형 중 하나의 값 또는 자리표시자가 아닌 경우 None을 반환합니다.

  • rank(x: Value | Placeholder | Type) -> Valuesize(shape(x))의 바로가기입니다.

  • shape(x: Value | Placeholder | Type) -> Valuemember_name를 통해 '유형의 함수' 섹션에 정의됩니다.

  • size(x: Value | Placeholder | Type) -> Valuereduce(lambda x, y: x * y, shape(x))의 바로가기입니다.

양자화 계산

  • def baseline_element_type(x: Value | Placeholder | Type) -> Typeelement_type(baseline_type(x))의 줄임말입니다.

  • baseline_type는 텐서 유형 및 양자화 텐서 유형에 정의되며 이를 '기준점', 즉 셰이프는 동일하지만 요소 유형의 양자화 매개변수가 기본값으로 재설정된 유형으로 변환합니다. 이는 텐서와 정규화된 텐서 유형을 일관되게 비교하는 편리한 트릭으로 사용되며, 이는 매우 자주 필요합니다. 정규화된 유형의 경우 정규화 매개변수를 무시하고 유형을 비교할 수 있습니다. 즉, shape, storage_type, expressed_type, storage_min, storage_max, quantization_dimension(축별 정규화 유형의 경우)가 모두 일치해야 하지만 scaleszero 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는 양자화된 텐서 유형에 정의되며 이를 부동 소수점 텐서 유형으로 변환합니다. 이는 저장소 유형의 정수 값을 나타내는 양자화된 요소를 양자화된 요소 유형과 연결된 영점 및 배율을 사용하여 표현된 유형의 상응하는 부동 소수점 값으로 변환하는 방식으로 이루어집니다.
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는 부동 소수점 텐서 유형에 정의되며 이를 양자화된 텐서 유형으로 변환합니다. 이는 정규화된 요소 유형과 연결된 제로 포인트 및 스케일을 사용하여 표현된 유형의 부동 소수점 값을 저장소 유형의 상응하는 정수 값으로 변환하는 방식으로 이루어집니다.
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는 부동 소수점 유형의 좌항과 양자화된 유형의 우항을 허용하는 하이브리드 연산자에 가중치 전용 양자화를 지정하는 데 사용됩니다. 정규화된 입력을 표현된 유형으로 역정규화하고 부동 소수점으로 계산을 실행합니다. 부동 소수점 좌항 텐서의 요소 유형과 정규화된 우항 텐서의 표현 유형은 동일해야 합니다.
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>여야 합니다. 도형 피연산자가 상수인 경우 정적 방식으로 확인할 수 있습니다. 결과 형태가 완전히 동적이면 불일치가 있을 수 없습니다.