1. 개요
Add 연산자는 두 입력 텐서를 원소 단위로 더해 하나의 출력 텐서를 생성한다.
입력 텐서의 shape가 서로 다를 경우 NumPy broadcasting 규칙을 적용하며, 두 입력의 dtype은 동일해야 한다.
수학적으로는 다음과 같이 표현된다.
z = x + y
Add는 단순한 산술 연산이지만 그래프 분석 관점에서는 다음과 같은 특징을 가진다.
- 두 입력을 하나의 출력으로 병합한다.
- 명시적인 입력 쌍 전체에 대해서는 선형 연산이다.
- 출력만으로는 두 입력을 각각 복원할 수 없다.
- 다른 한쪽 입력을 알고 있다면 이상적인 산술에서는 나머지 입력을 복원할 수 있다.
- 실제 부동소수점 연산에서는 반올림으로 인해 복원이 실패할 수 있다.
2. 입력 및 출력 규칙
2.1 입력 개수
Add는 정확히 두 개의 입력을 요구한다.
Add(x, y)
입력 개수가 두 개가 아니면 연산은 유효하지 않다.
2.2 dtype 규칙
두 입력은 동일한 dtype을 가져야 한다.
x.dtype == y.dtype
현재 프레임워크에서는 NumPy와 같은 암묵적 dtype promotion을 허용하지 않는다.
예를 들어 다음 입력은 허용되지 않는다.
float32 + float64
int32 + float32
이 정책은 정적 분석과 실제 실행 결과 사이의 불일치를 줄이고 연산 의미를 단순하게 유지하기 위한 것이다.
2.3 shape 규칙
두 입력의 shape는 NumPy broadcasting 규칙과 호환되어야 한다.
예를 들어 다음 연산은 유효하다.
(3, 4) + (3, 4) → (3, 4)
(3, 4) + (4,) → (3, 4)
(3, 1) + (3, 4) → (3, 4)
(1,) + (2, 3) → (2, 3)
다음과 같이 broadcasting이 불가능한 shape 조합은 유효하지 않다.
(3, 4) + (2, 4)
출력 shape는 다음과 같이 결정된다.
output_shape = np.broadcast_shapes(left.shape, right.shape)
3. 대수적 의미
3.1 결합 입력에 대한 선형성
Add를 두 입력의 결합 공간에서 정의된 함수로 보면 다음과 같다.
F(x, y) = x + y
두 입력 쌍 (x₁, y₁), (x₂, y₂)와 스칼라 a, b에 대해 다음이 성립한다.
F(a(x₁, y₁) + b(x₂, y₂))
= F(ax₁ + bx₂, ay₁ + by₂)
= ax₁ + bx₂ + ay₁ + by₂
= a(x₁ + y₁) + b(x₂ + y₂)
= aF(x₁, y₁) + bF(x₂, y₂)
따라서 Add는 두 입력을 모두 변수로 보는 결합 입력 공간에서 선형 연산이다.
"linear": True
"nonlinear": False
보다 정확한 의미 표현은 다음과 같다.
jointly linear
즉, Add의 선형성은 명시적인 두 입력 (x, y) 전체를 대상으로 한다.
3.2 한 입력을 상수로 보는 경우
한쪽 입력을 상수 c로 고정하면 연산은 다음과 같이 보인다.
f(x) = x + c
c ≠ 0이라면 이 함수는 일반적인 의미의 선형 함수가 아니다.
f(0) = c ≠ 0
이 경우에는 affine transformation이다.
따라서 다음 두 상황을 구분해야 한다.
Add(x, y)
두 입력을 모두 변수로 보면 jointly linear이다.
Add(x, constant)
한 입력을 상수로 고정하면 다른 입력에 대해서는 affine이다.
프레임워크의 linear=True는 다음 기준으로 해석한다.
연산자의 모든 명시적 입력을 변수로 간주했을 때 선형인가?
이 기준을 사용하면 Add는 선형 연산으로 분류할 수 있다.
4. 병합 의미
Add는 두 개의 입력 텐서를 하나의 출력 텐서로 결합한다.
(x, y) → x + y
이 병합은 원소 단위 합에 의해 이루어진다.
"merge_kind": "elementwise_sum"
이는 Concat과 같은 구조적 결합과 다르다.
Concat은 각 입력의 값을 서로 다른 위치에 보존하지만, Add는 동일한 출력 위치에서 두 입력값을 합산한다.
Concat:
(x, y) → [x, y]
Add:
(x, y) → x + y
따라서 Add는 입력 정보를 위치적으로 보존하는 병합이 아니라, 값을 중첩시키는 병합이다.
5. 다대일 성질
Add는 입력 쌍 전체에 대해 다대일 함수이다.
동일한 출력이 여러 입력 쌍으로부터 생성될 수 있다.
1 + 2 = 3
0 + 3 = 3
-1 + 4 = 3
텐서에서도 동일한 성질이 나타난다.
x₁ + y₁ = z
x₂ + y₂ = z
서로 다른 (x₁, y₁)와 (x₂, y₂)가 동일한 출력 z를 만들 수 있다.
따라서 다음 의미는 보장된다.
SemanticFact(
"many_to_one",
True,
EvidenceLevel.GUARANTEED,
)
보다 명확한 설명은 다음과 같다.
서로 다른 결합 입력 쌍이 동일한 출력 텐서를 생성할 수 있다.
6. 비가역성
6.1 출력만으로 두 입력을 복원하는 경우
출력이 다음과 같이 주어졌다고 하자.
z = x + y
출력 z만 알고 있을 때는 x와 y를 각각 결정할 수 없다.
x = arbitrary tensor
y = z - x
임의의 x를 선택하면 그에 대응하는 y를 만들 수 있다.
따라서 하나의 출력에 대응하는 입력 쌍이 여러 개 존재한다.
"irreversible": True
그리고 다음 의미가 보장된다.
SemanticFact(
"joint_input_recoverable",
False,
EvidenceLevel.GUARANTEED,
)
여기서 joint_input_recoverable=False는 다음을 의미한다.
출력만으로 원래의 두 입력 전체를 동시에 복원할 수 없다.
6.2 다른 한쪽 입력이 주어진 경우
출력 z와 입력 y가 주어지면 수학적으로 x는 다음과 같이 복원할 수 있다.
x = z - y
마찬가지로 출력 z와 입력 x가 주어지면 y를 복원할 수 있다.
y = z - x
따라서 이상적인 정확한 산술에서는 다음 성질이 성립한다.
"recoverable_given_other_input": True
하지만 이 성질은 실제 유한 정밀도 연산에서 항상 보장되지는 않는다.
7. 유한 정밀도에 의한 정보 손실
부동소수점 덧셈에서는 작은 값이 큰 값에 흡수될 수 있다.
예를 들어 float32에서 다음 연산을 생각할 수 있다.
x = np.float32(1.0)
y = np.float32(1e20)
z = x + y
recovered_x = z - y
이론적으로는 다음이 기대된다.
recovered_x = 1.0
하지만 실제 부동소수점 표현에서는 x가 y에 비해 너무 작기 때문에 덧셈 과정에서 사라질 수 있다.
z ≈ y
z - y = 0
따라서 다음 식은 일반적으로 보장되지 않는다.
(x + y) - y = x
이는 Add가 수학적으로는 다른 입력이 알려진 경우 역산 가능하지만, 실제 수치 연산에서는 정보 손실을 일으킬 수 있음을 의미한다.
그러므로 다음 의미를 GUARANTEED로 정의하는 것은 부정확하다.
SemanticFact(
"recoverable_given_other_input",
True,
EvidenceLevel.GUARANTEED,
)
권장되는 정의는 다음과 같다.
SemanticFact(
"recoverable_given_other_input",
True,
EvidenceLevel.IDEALIZED,
"Recoverable by subtraction under exact arithmetic; "
"finite-precision rounding or overflow may destroy information.",
)
IDEALIZED 수준을 사용하지 않는다면 CONDITIONAL로 표현할 수 있다.
EvidenceLevel.CONDITIONAL
8. 정수 연산과 overflow
고정 폭 정수 연산에서도 overflow가 발생할 수 있다.
예를 들어 8비트 signed integer의 표현 범위는 다음과 같다.
-128 ~ 127
다음 덧셈은 표현 범위를 벗어난다.
120 + 20 = 140
실제 실행 환경에서는 overflow 정책에 따라 wraparound, saturation 또는 오류가 발생할 수 있다.
따라서 정수 덧셈에서도 수학적인 정수 연산과 실제 실행 결과를 구분해야 한다.
다만 wraparound가 정확한 modular arithmetic으로 정의되어 있다면, 다른 한쪽 입력을 알고 있을 때 modular subtraction을 통해 원래 값을 복원할 수 있다.
복원 가능성은 프레임워크가 채택한 overflow 의미에 의존한다.
9. Broadcasting과 복원 가능성
다음 입력을 생각할 수 있다.
x.shape = (3, 1)
y.shape = (3, 4)
덧셈 과정에서 x는 다음과 같이 broadcasting된다.
broadcast(x).shape = (3, 4)
출력은 다음과 같다.
z = broadcast(x) + y
y를 알고 있으면 다음을 계산할 수 있다.
broadcast(x) = z - y
이상적인 산술에서는 broadcasting된 결과의 반복 축을 제거해 원래 x를 복원할 수 있다.
하지만 원래 shape를 복원하려면 입력의 TensorSpec이 필요하다.
x.shape = (3, 1)
따라서 broadcasting이 포함된 복원 가능성은 다음 정보를 전제로 한다.
- 다른 한쪽 입력
- 출력
- 원래 입력의 shape 또는 TensorSpec
- 정확한 산술
현재 그래프가 각 입력의 TensorSpec을 보존하므로 semantic fact의 이름을 간단히 유지할 수 있다.
"recoverable_given_other_input"
보다 엄밀한 이름은 다음과 같다.
"recoverable_given_other_input_and_input_spec"
10. 붕괴 관점에서의 해석
Add는 두 입력 공간의 정보를 동일한 출력 공간에 중첩한다.
입력이 각각 n개의 원소를 가진다고 하면 결합 입력은 총 2n개의 스칼라 값을 가진다.
(x, y) ∈ Rⁿ × Rⁿ
출력은 n개의 스칼라 값만 가진다.
z ∈ Rⁿ
따라서 Add는 결합 입력 공간을 더 낮은 차원의 출력 공간으로 사상한다.
R²ⁿ → Rⁿ
이 과정에서 두 입력 사이의 구분 정보가 사라진다.
예를 들어 출력의 특정 값이 3이라는 사실만으로는 다음 중 어떤 입력 조합이 사용되었는지 알 수 없다.
1 + 2
0 + 3
-1 + 4
이 의미에서 Add는 다음과 같은 붕괴적 특징을 가진다.
두 입력의 개별 기여를 하나의 합으로 압축하면서 입력 간 구분 정보를 제거한다.
다만 이 붕괴는 모든 정보를 무조건 제거하는 것은 아니다.
한쪽 입력이 별도로 보존되어 있다면 다른 입력을 역산할 수 있다.
따라서 Add의 붕괴는 다음과 같이 분류할 수 있다.
조건부 복원 가능한 병합 붕괴
또는 다음과 같이 표현할 수 있다.
jointly irreversible, conditionally recoverable merge
11. 권장 분석 정의
def analyze(
self,
inputs: list[TensorSpec],
output: TensorSpec,
) -> AnalysisDict:
return {
"op_type": self.op_type,
"parameter_count": 0,
"linear": True,
"nonlinear": False,
"irreversible": True,
"merge_kind": "elementwise_sum",
}
각 항목의 의미는 다음과 같다.
| parameter_count | 0 | 학습 가능한 파라미터가 없음 |
| linear | True | 두 명시적 입력의 결합 공간에 대해 선형 |
| nonlinear | False | 비선형 활성화나 비선형 변환이 없음 |
| irreversible | True | 출력만으로 두 입력 전체를 복원할 수 없음 |
| merge_kind | elementwise_sum | 원소 단위 합을 통한 입력 병합 |
항목값의미
12. 권장 semantic facts 정의
def semantic_facts(
self,
inputs: list[TensorSpec],
output: TensorSpec,
) -> tuple[SemanticFact, ...]:
return (
SemanticFact(
"algebraic_kind",
"jointly_linear_merge",
EvidenceLevel.GUARANTEED,
"Linear with respect to the joint input pair (x, y).",
),
SemanticFact(
"many_to_one",
True,
EvidenceLevel.GUARANTEED,
"Distinct joint input pairs can produce the same output tensor.",
),
SemanticFact(
"joint_input_recoverable",
False,
EvidenceLevel.GUARANTEED,
"The output alone does not determine both original inputs.",
),
SemanticFact(
"recoverable_given_other_input",
True,
EvidenceLevel.IDEALIZED,
"Recoverable by subtraction under exact arithmetic; "
"finite-precision rounding or overflow may destroy information.",
),
SemanticFact(
"merge_kind",
"elementwise_sum",
EvidenceLevel.GUARANTEED,
),
)
13. EvidenceLevel 해석
Add의 의미를 정확히 표현하려면 수학적 성질과 실제 실행 성질을 구분할 필요가 있다.
권장되는 증거 수준은 다음과 같다.
class EvidenceLevel(Enum):
GUARANTEED = "guaranteed"
CONDITIONAL = "conditional"
IDEALIZED = "idealized"
HEURISTIC = "heuristic"
EMPIRICAL = "empirical"
GUARANTEED
실제 프레임워크의 연산 의미에서도 항상 성립하는 성질이다.
예:
Add는 두 입력을 하나의 출력으로 병합한다.
출력만으로 원래 입력 쌍 전체를 복원할 수 없다.
CONDITIONAL
특정 dtype, shape, 값 또는 실행 조건에서만 성립한다.
예:
overflow가 발생하지 않는 정수 덧셈에서는 다른 입력을 이용해 복원할 수 있다.
IDEALIZED
무한 정밀도 실수 또는 정확한 대수 연산을 가정할 때 성립한다.
예:
x + y와 y가 주어지면 x를 정확히 복원할 수 있다.
HEURISTIC
수학적으로 보장되지는 않지만 일반적으로 관찰되는 경향이다.
EMPIRICAL
실제 실행 데이터나 runtime profiling을 통해 관측된 성질이다.
14. 최종 의미 요약
Add는 두 입력을 원소 단위로 합산하는 jointly linear merge 연산이다.
수학적으로는 선형 연산이지만 두 입력의 개별 정보를 하나의 합에 중첩하므로 출력만으로 원래 입력 쌍을 복원할 수 없다.
따라서 결합 입력에 대해서는 다대일이며 비가역적이다.
다른 한쪽 입력이 보존되어 있다면 이상적인 정확한 산술에서는 나머지 입력을 뺄셈으로 복원할 수 있다.
그러나 실제 부동소수점 및 고정 폭 정수 연산에서는 반올림과 overflow로 인해 정보가 손실될 수 있으므로, 이 복원 가능성은 실행 수준에서 보장되는 성질이 아니다.
Add의 핵심 의미는 다음과 같이 정리할 수 있다.
대수적 성질:
jointly linear
병합 방식:
elementwise sum
정보 구조:
many-to-one
전체 입력 복원:
불가능
한쪽 입력이 주어진 경우:
이상적인 산술에서는 복원 가능
붕괴적 특징:
두 입력의 개별 기여를 하나의 합으로 중첩하여
입력 간 구분 정보를 제거함
'AI Compiler framework' 카테고리의 다른 글
| Linear 연산 합성 실험 결과 분석 (0) | 2026.07.06 |
|---|---|
| MatMul 연산자 의미 정의 (1) | 2026.07.06 |
| AI Compiler 프로젝트 개발 재시작 방향 (0) | 2026.07.05 |
| AICF / MCIR 에서 패턴의 새로운 정의 (0) | 2026.03.23 |
| 단순 최적화 프레임과 최적화 가능성의 조건을 재정의하는 프레임의 두 층으로 나눈 작업 분류 (0) | 2026.03.23 |