Skip to content

[AutoDiff] Incorrect derivative value for some functions containing ternary operators #81777

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
kovdan01 opened this issue May 26, 2025 · 3 comments
Labels
AutoDiff bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler itself SILGen Area → compiler: The SIL generation stage swift 6.2 unexpected behavior Bug: Unexpected behavior or incorrect output

Comments

@kovdan01
Copy link
Contributor

kovdan01 commented May 26, 2025

Description

For some functions with ternary operators, derivative is calculated incorrectly (ternary function in reproducer). When changing ternary operators to if statements, the calculation becomes correct (ifelse function in reproducer). The function results themselves are calculated correctly for both cases.

Particularly, for the reproducer, the expected gradient value with x0<=x1 is (0,0), while we get (1/x0,0).

Reproduction

import _Differentiation

// x0 > x1  && x1 < 0:  1 - x1 / x0
// x0 > x1  && x1 >= 0: 1 + x1 / x0
// x0 <= x1:            1
@differentiable(reverse)
func ternary(_ x0: Float, _ x1: Float) -> Float {
  let t1 = x1 + x0;
  let t2 = x0 - x1;
  let t4 = x0 > x1 ? t1 : x0;
  let t6 = 1 / x0;
  let t5 = x0 > t4 ? t2 : t4;
  let t7 = t5 * t6;
  return t7;
}

func actual_gradient(_ x0: Float, _ x1: Float) -> (Float, Float) {
  if x0 > x1 {
    if x1 < 0 {
      return (x1 / (x0 * x0), -1 / x0)
    }
    return (-x1 / (x0 * x0), 1 / x0)
  }
  // FIXME: this should be (0, 0)
  return (1 / x0, 0)
}

@differentiable(reverse)
func ifelse(_ x0: Float, _ x1: Float) -> Float {
  let t1 = x1 + x0;
  let t2 = x0 - x1;
  let t4 : Float = if x0 > x1 { t1 } else { x0 }
  let t6 = 1 / x0;
  let t5 : Float = if x0 > t4 { t2 } else { t4 }
  let t7 = t5 * t6;
  return t7;
}

func true_gradient(_ x0: Float, _ x1: Float) -> (Float, Float) {
  if x0 > x1 {
    if x1 < 0 {
      return (x1 / (x0 * x0), -1 / x0)
    }
    return (-x1 / (x0 * x0), 1 / x0)
  }
  return (0, 0)
}

for (x0, x1) in [(Float(5), Float(9)), (Float(-2), Float(1))] {
  print("       ternary'(\(x0), \(x1)) = \(gradient(at: x0, x1, of: ternary))")
  print("actual_gradient(\(x0), \(x1)) = \(actual_gradient(x0, x1))")
  print("        ifelse'(\(x0), \(x1)) = \(gradient(at: x0, x1, of: ifelse))")
  print("  true_gradient(\(x0), \(x1)) = \(true_gradient(x0, x1))")
  print("")
}

Output:

       ternary'(5.0, 9.0) = (0.2, 0.0)
actual_gradient(5.0, 9.0) = (0.2, 0.0)
        ifelse'(5.0, 9.0) = (1.4901161e-08, 0.0)
  true_gradient(5.0, 9.0) = (0.0, 0.0)

       ternary'(-2.0, 1.0) = (-0.5, 0.0)
actual_gradient(-2.0, 1.0) = (-0.5, 0.0)
        ifelse'(-2.0, 1.0) = (0.0, 0.0)
  true_gradient(-2.0, 1.0) = (0.0, 0.0)

Expected behavior

Gradient value for ternary and ifelse should be equal (particularly, for x0<=x1, we should have (0,0) gradient value for both).

Environment

Swift version 6.2-dev (LLVM dc6a0c133fea15e, Swift efecad888e1731c)
Target: x86_64-unknown-linux-gnu
Build config: +assertions

Additional information

No response

@kovdan01 kovdan01 added bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. AutoDiff labels May 26, 2025
@kovdan01
Copy link
Contributor Author

Tagging @asl @JaapWijnen

@AnthonyLatsis AnthonyLatsis added compiler The Swift compiler itself SILGen Area → compiler: The SIL generation stage unexpected behavior Bug: Unexpected behavior or incorrect output swift 6.2 labels May 26, 2025
@JaapWijnen
Copy link
Contributor

Good find! Thanks @kovdan01

@kovdan01
Copy link
Contributor Author

kovdan01 commented May 27, 2025

The SIL of ternary and ifelse functions themselves (unoptimized, generated w/o -O flag) is exactly the same. The reverse-mode derivatives look almost identical, so the problem is probably in incorrectly generated pullback (the pullbacks are pretty different). I'll publish an update here when I find the particular incorrect place in the pullback of ternary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
AutoDiff bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler itself SILGen Area → compiler: The SIL generation stage swift 6.2 unexpected behavior Bug: Unexpected behavior or incorrect output
Projects
None yet
Development

No branches or pull requests

3 participants