Entity.DomainCondition and evaluation disagree about where log is defined, in both directions, and one of the disagreements is a wrong answer.
Measured on master (f2594259), .NET 10, default MathS.Settings.Codomain (Domain.Complex):
| expression |
DomainCondition |
Evaled |
what it should be |
log(b, x) |
b > 0 and not b = 1 and x > 0 |
|
|
log(-3, -3) |
False |
1 |
1 — the condition is wrong |
log(-1, -1) |
False |
1 |
1 — likewise |
log(1, 1) |
False |
0 |
NaN — the value is wrong |
log(1, 2) |
False |
+oo |
unsigned/complex infinity |
log(0, 2) |
False |
0 |
|
log(2, 0) |
False |
-oo |
|
Two separate defects, and they point opposite ways.
1. The declared domain is too small
log_b(z) is ln z / ln b, and over the complex plane that is defined and single-valued whenever
z != 0 and ln b != 0 — so for a negative base and argument too: ln(-3)/ln(-3) is exactly
1. DomainCondition says False, i.e. "this expression has a value nowhere", while Evaled
returns 1 and Simplify returns 1.
Logf's IntrinsicCondition is written for the real reading (b > 0, x > 0) while the default
codomain is complex, and nothing reconciles the two. sqrt goes the other way — DomainCondition
of sqrt(x) is True, the complex reading — so the two functions do not even agree with each
other.
Why it matters beyond tidiness. DomainCondition is what a rewrite is supposed to consult to
decide whether it may fire and what condition to attach; that is obligation O5 in
SimplificationContract.md. A rule that
trusts it inherits the over-strong condition, which is exactly what has happened to
log_b(b) -> 1:
"log(x, x)".Simplify() => 1 provided x > 0
At x = -3 the expression is 1 and the simplification says it is undefined. That is the mirror of
the usual defect — it turns a value into undefined rather than the other way round — and it was
found by work/boundcheck, which is why this issue exists.
2. log(1, 1) is 0, and should be NaN
ln 1 is 0 exactly, so log_1(1) is 0/0. SymPy gives nan and Mathematica Indeterminate.
Returning 0 is a wrong answer in the sense AGENTS.md
means it: a definite value where none exists. It is not shielded by the domain condition, which
already excludes b = 1 — evaluation simply does not consult it.
log(1, 2) giving +oo is a milder version of the same thing: dividing by ln 1 = 0 has no signed
answer, and both SymPy and Mathematica report an unsigned complex infinity.
What needs deciding, and what does not
The wrong answer at log(1, 1) needs no decision — 0/0 is not 0.
The domain is a decision, and it is the one #721
is about: DomainCondition is fixed at construction and cannot read
MathS.Settings.Codomain, so it cannot say "defined here, given that we are doing real analysis".
Either it becomes codomain-aware, or it commits to the complex reading and the real-analysis
restriction moves to where the codomain is known. Picking one is a maintainer's call; what is not in
question is that a single expression should not be declared undefined everywhere and simultaneously
evaluate to 1.
Related: #887 records the log(x, x)
symptom, and #884 is the other logarithm
rule whose assumption is not stated.
Entity.DomainConditionand evaluation disagree about wherelogis defined, in both directions, and one of the disagreements is a wrong answer.Measured on
master(f2594259), .NET 10, defaultMathS.Settings.Codomain(Domain.Complex):DomainConditionEvaledlog(b, x)b > 0 and not b = 1 and x > 0log(-3, -3)False11— the condition is wronglog(-1, -1)False11— likewiselog(1, 1)False0NaN— the value is wronglog(1, 2)False+oolog(0, 2)False0log(2, 0)False-ooTwo separate defects, and they point opposite ways.
1. The declared domain is too small
log_b(z)isln z / ln b, and over the complex plane that is defined and single-valued wheneverz != 0andln b != 0— so for a negative base and argument too:ln(-3)/ln(-3)is exactly1.DomainConditionsaysFalse, i.e. "this expression has a value nowhere", whileEvaledreturns
1andSimplifyreturns1.Logf'sIntrinsicConditionis written for the real reading (b > 0,x > 0) while the defaultcodomain is complex, and nothing reconciles the two.
sqrtgoes the other way —DomainConditionof
sqrt(x)isTrue, the complex reading — so the two functions do not even agree with eachother.
Why it matters beyond tidiness.
DomainConditionis what a rewrite is supposed to consult todecide whether it may fire and what condition to attach; that is obligation O5 in
SimplificationContract.md. A rule that
trusts it inherits the over-strong condition, which is exactly what has happened to
log_b(b) -> 1:At
x = -3the expression is1and the simplification says it is undefined. That is the mirror ofthe usual defect — it turns a value into undefined rather than the other way round — and it was
found by
work/boundcheck, which is why this issue exists.2.
log(1, 1)is0, and should beNaNln 1is0exactly, solog_1(1)is0/0. SymPy givesnanand MathematicaIndeterminate.Returning
0is a wrong answer in the sense AGENTS.mdmeans it: a definite value where none exists. It is not shielded by the domain condition, which
already excludes
b = 1— evaluation simply does not consult it.log(1, 2)giving+oois a milder version of the same thing: dividing byln 1 = 0has no signedanswer, and both SymPy and Mathematica report an unsigned complex infinity.
What needs deciding, and what does not
The wrong answer at
log(1, 1)needs no decision —0/0is not0.The domain is a decision, and it is the one #721
is about:
DomainConditionis fixed at construction and cannot readMathS.Settings.Codomain, so it cannot say "defined here, given that we are doing real analysis".Either it becomes codomain-aware, or it commits to the complex reading and the real-analysis
restriction moves to where the codomain is known. Picking one is a maintainer's call; what is not in
question is that a single expression should not be declared undefined everywhere and simultaneously
evaluate to
1.Related: #887 records the
log(x, x)symptom, and #884 is the other logarithm
rule whose assumption is not stated.