-
Notifications
You must be signed in to change notification settings - Fork 45
Guard LogSimplify rewrites against domain narrowing (#2570) #2576
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
Changes from 4 commits
86eef28
4f2c333
ba6fd78
ab6cee3
9cc0a7b
e57a9da
9bab829
6b6f1e5
aa06597
b76081e
51ec6b6
ff1c40f
48aaf2b
5660c46
b30b8d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27760,7 +27760,11 @@ struct LogSimplify final | |
|
|
||
| { // log(pow(x, y)) -> y * log(x) | ||
| auto defOp = op.getOperand().getDefiningOp<stablehlo::PowOp>(); | ||
| if (defOp) { | ||
| // Only sound when the base x is provably non-negative. For x < 0, | ||
| // log(pow(x, y)) can be a finite real (e.g. even integer y, where | ||
| // pow(x, y) > 0) while y * log(x) is NaN; likewise y = 0 gives | ||
| // log(pow(x, 0)) = 0 but 0 * log(x) = NaN. See issue #2570. | ||
| if (defOp && guaranteedNonNegativeResult(defOp.getLhs(), rewriter)) { | ||
| rewriter.replaceOpWithNewOp<stablehlo::MulOp>( | ||
| op, defOp.getRhs(), | ||
| stablehlo::LogOp::create(rewriter, op.getLoc(), defOp.getLhs())); | ||
|
|
@@ -27773,23 +27777,35 @@ struct LogSimplify final | |
| if (defOp) { | ||
| auto lhs = defOp.getLhs(); | ||
| auto rhs = defOp.getRhs(); | ||
| if (lhs == rhs) { // log(mul(a, a)) -> 2 * log(a) | ||
| if (lhs == rhs) { // log(mul(a, a)) -> 2 * log(abs(a)) | ||
| // a*a is non-negative for every real a, but 2 * log(a) is NaN for | ||
| // a < 0. Take the log of |a| so the rewrite keeps log(a*a)'s domain | ||
| // (log(a*a) = 2 * log|a|). See issue #2570. | ||
| rewriter.replaceOpWithNewOp<stablehlo::MulOp>( | ||
| op, | ||
| stablehlo::ConstantOp::create( | ||
| rewriter, op.getLoc(), lhs.getType(), | ||
| cast<ElementsAttr>(makeAttr(lhs.getType(), 2))), | ||
| stablehlo::LogOp::create(rewriter, op.getLoc(), lhs)); | ||
| stablehlo::LogOp::create( | ||
| rewriter, op.getLoc(), | ||
| stablehlo::AbsOp::create(rewriter, op.getLoc(), lhs.getType(), | ||
| lhs))); | ||
| return success(); | ||
| } | ||
|
|
||
| if (anyOperandIsConstant(defOp) && | ||
| !allOperandsAreConstant(defOp)) { // log(mul(a, b)) -> log(a) + | ||
| // log(b) if a or b is constant | ||
| rewriter.replaceOpWithNewOp<stablehlo::AddOp>( | ||
| op, stablehlo::LogOp::create(rewriter, op.getLoc(), lhs), | ||
| stablehlo::LogOp::create(rewriter, op.getLoc(), rhs)); | ||
| return success(); | ||
| // Only sound when the constant operand is non-negative: a negative | ||
| // constant makes log(const) NaN where log(a*b) was a finite real | ||
| // (a < 0 makes both sides NaN, so they still agree). Issue #2570. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you rename this from isValidLogSplitConstant to isPositiveFiniteConstant for clarity |
||
| Value cst = matchPattern(lhs, m_Constant()) ? lhs : rhs; | ||
| if (guaranteedNonNegativeResult(cst, rewriter)) { | ||
| rewriter.replaceOpWithNewOp<stablehlo::AddOp>( | ||
| op, stablehlo::LogOp::create(rewriter, op.getLoc(), lhs), | ||
| stablehlo::LogOp::create(rewriter, op.getLoc(), rhs)); | ||
| return success(); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fails when the constant is exactly 0 and the variable is negative. Because guaranteedNonNegativeResult allows the constant to be exactly 0 Similar problem as before, and I still think this should not necessarily block the PR since the status quo is actually a good bit worse. |
||
| } | ||
| } | ||
| } | ||
|
|
@@ -27839,10 +27855,16 @@ struct LogSimplify final | |
| if (anyOperandIsConstant(defOp) && | ||
| !allOperandsAreConstant(defOp)) { // log(div(a, b)) -> log(a) - | ||
| // log(b) if a or b is constant | ||
| rewriter.replaceOpWithNewOp<stablehlo::SubtractOp>( | ||
| op, stablehlo::LogOp::create(rewriter, op.getLoc(), lhs), | ||
| stablehlo::LogOp::create(rewriter, op.getLoc(), rhs)); | ||
| return success(); | ||
| // Only sound when the constant operand is non-negative: a negative | ||
| // constant makes log(const) NaN where log(a/b) was a finite real. | ||
| // Issue #2570. | ||
| Value cst = matchPattern(lhs, m_Constant()) ? lhs : rhs; | ||
| if (guaranteedNonNegativeResult(cst, rewriter)) { | ||
| rewriter.replaceOpWithNewOp<stablehlo::SubtractOp>( | ||
| op, stablehlo::LogOp::create(rewriter, op.getLoc(), lhs), | ||
| stablehlo::LogOp::create(rewriter, op.getLoc(), rhs)); | ||
| return success(); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same problem and still do not think this should be blocking Fails when the numerator ( |
||
| } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment here claims that this would fix the log(pow(x, 0)) case, but it actually does not. In the specific case where$x = 0$ and $y = 0$ :If we start with
$\log(\text{pow}(0, 0)) = \log(1) = 0$ ,$0 \cdot \log(0)$ ,$\log(0)$ is $-\infty$ ). That is mathematically incorrect. Soooooooooooooo we still have a correctness bug here, actually. Proving that $x$ or $y$ is never $0$ is kind of hard, so we might genuinely need to put that behind a flag, or just outright remove this rewrite, or try to prove that $x$ is strictly greater than zero (which, I might be wrong, is only provable in very specific scenarios, like the definingOp of $x$ or $y$ being an exponential op which can never be $0$ ). Nevertheless, I don't think you need to fix this in this PR, since having guaranteedNonNegativeResult is already a massive improvement. So, I think I just need you to update the comment to include this info.
our pattern rewrites it into
which evaluates to NaN (because
(cc @wsmoses I think this needs your authority on how to deal with this)