Repository navigation
DomainCondition says log(-3, -3) is defined nowhere and it evaluates to 1; log(1, 1) is 0 and should be NaN #890
Description
Activity
Yes, it should become codomain-aware.
- added a commit that references this issue
on Aug 11, 2026 The domain half of this is answered on #721 with what SymPy and Mathematica actually do, measured rather than recalled: neither carries a real-analysis domain on the expression. SymPy evaluates
log(-3, -3)to1exactly as this library does, and gets the real domain fromcontinuous_domain(f, x, S.Reals)— a query with the reading as an argument. Mathematica'sFunctionDomain[f, x, dom]is the same shape, defaulting toReals.So the fork's second option — parameterise the query by the reading — is the ordinary design elsewhere, and it also makes this library's
log(x, x) -> 1 provided x > 0stronger: over ℂ the condition isnot x = 1 and not x = 0, which removes one ofboundcheck's two remaining disagreements. The comment on #721 lists what else it wins back, including the two limits #902 lost.- added a commit that references this issue
on Aug 13, 2026 Re-measured on
masterat2d128f3c. Both defects are fixed, by #916 (51194ce8, "Let the logarithm's domain follow the reading (#721, #890)"), and this looks closeable.expression DomainConditionthennow Evaledthennow log(b, x)b > 0 and not b = 1 and x > 0not b = 0 and not b = 1 and not x = 0log(-3, -3)FalseTrue11log(-1, -1)FalseTrue11log(1, 1)FalseFalse0NaNlog(1, 2)FalseFalse+ooNaNlog(0, 2)FalseFalse00log(2, 0)FalseFalse-oo-ooThe condition now reads
Codomain < Domain.Complex ? Base > 0 & !Base.EqualTo(1) & Antilogarithm > 0 : !Base.EqualTo(0) & !Base.EqualTo(1) & !Antilogarithm.EqualTo(0)
which is @Happypig375's "it should become codomain-aware" done literally — the real reading under a real codomain, the complex one otherwise. So
log(-3, -3)is now declared defined and still evaluates to1, andlog(1, 1)isNaNrather than the wrong0.On the two rows that still show a disagreement.
log(0, 2)evaluates to0andlog(2, 0)to-oowhile the condition saysFalsefor both. That is deliberate, and the reason is now written where the decision is:The zero cases are stated rather than read off evaluation, which returns the extended-real
-ooforln(0)and so would call it defined.-oois not a complex number, and taking it for one loses conditions that are needed downstream:ln(x) * 0is0only away fromx = 0, since-oo * 0isNaN.So evaluation returning a value is not the definedness oracle, and those two rows are the condition being right rather than the two disagreeing.
One row does not match what the issue asked for, and it is minor:
log(1, 2)isNaN, where the issue said it should be unsigned/complex infinity.ln 2 / ln 1isln 2 / 0, so an unsigned infinity is defensible andNaNis the library's usual answer for a division by zero that is not a limit. If that is worth changing it is a smaller and separate question than this issue was — happy to file it if you want it tracked.Closing is yours; I have not changed anything here.
Both defects are fixed. Re-measured on
masterat3ac24bc2, same settings — defaultMathS.Settings.Codomain(Domain.Complex):expression DomainConditionthennow Evaledthennow log(b, x)b > 0 and not b = 1 and x > 0not b = 0 and not b = 1 and not x = 0log(-3, -3)FalseTrue11log(-1, -1)FalseTrue11log(1, 1)FalseFalse0NaNlog(1, 2)FalseFalse+ooNaNlog(0, 2)FalseFalse00log(2, 0)FalseFalse-oo-oo§1 — the declared domain was too small. It is now the complex reading this issue asked for:
not b = 0 and not b = 1 and not x = 0, which admits a negative base and a negative argument. And the rule that inherited the over-strong condition inherits the right one:"log(x, x)".Simplify() was: 1 provided x > 0 -- undefined at x = -3, where it is 1 now: 1 provided not x = 0 and not x = 1§2 —
log(1, 1)was0. It isNaN.On
log(1, 2)I suggested unsigned/complex infinity. It is
NaN, and I now think that is the better answer here rather than a shortfall, because it is the one this library already gives for the same arithmetic:1/0 => NaN -1/0 => NaN 0/0 => NaN ln(1) => 0 ln(0) => -oolog_1(2)isln 2 / ln 1=ln 2 / 0, so it lands wherever1/0lands. Adoptingzoofor this one function would have made it disagree with division. The remainingFalsedomain conditions against a returned0or-ooare the separate and deliberate distinction that-oois a value but not a complex number -- evaluation is not the definedness oracle.Closing as fixed unless you read those last two rows differently. Found while sweeping the open issues before proposing a release, so this was measured rather than assumed.
Re-measured on
masterat6b93b401(2.3.0), .NET 10, defaultDomain.Complex. Both defects named in the title are fixed — by #893 (logbehaving like the division it is defined as) and #916 (the logarithm's domain following the reading):expression DomainConditionthennow Evaledthennow log(-3, -3)FalseTrue11log(-1, -1)FalseTrue11log(1, 1)FalseFalse0NaNSo §1 (the declared domain too small for a negative base and argument) and §2 (
log(1, 1)answering0where nothing is there) are both answered, in the directions the issue argued for.What is left is narrower than the title and is still a disagreement. Two rows where
DomainConditionsays the expression has a value nowhere and evaluation returns one:log(0, 2) DomainCondition = False Evaled = 0 log(2, 0) DomainCondition = False Evaled = -ooBoth come from reading
ln(0)as-ooand then dividing, which is a limit rather than a value — so here it is arguably evaluation that is over-eager rather than the domain that is over-strict, which is the opposite of §1's diagnosis. And one row moved without being asked to:log(1, 2)was+ooand is nowNaN.ln(2)/ln(1)is complex infinity, which this library has no way to say (#217), soNaNand+ooare both wrong andNaNis the less wrong of the two — worth recording rather than leaving as a silent change.Keeping this open for that residue. Closing it would claim the domain and evaluation now agree about
log, and on those three rows they do not.Re-measured on
masterata2edfc33during a pre-release sweep. Both defects in the title are
gone.expression DomainConditionEvaledthis issue said log(-3, -3)True1condition was False— wronglog(1, 1)FalseNaNvalue was 0— wronglog(b, x)not b = 0 and not b = 1 and not x = 0was b > 0 and not b = 1 and x > 0Logf.IntrinsicConditionstates both readings now and selects on the node's ownCodomain:
the complex one asks only that both logarithms exist and that the denominator is not zero, which
is what makes a negative base and a negative antilogarithm fine.And the consequence this issue called out —
"log(x, x)".Simplify()giving1 provided x > 0,
undefined atx = -3where the expression is1— is gone with it. The rule reads the node's
ownDomainConditionrather than stating a reading inside itself, so it follows the reading
instead of fixing one.One thing left, and it is not what the title is about
domain(log(x, x))isnot x = 0 and not x = 1 and not x = 0— the same conjunct twice,
because base and antilogarithm are the same expression. It is a tidiness matter rather than a
wrong answer, and it is not free to fix:Andf.InnerSimplifyalready does idempotence for the
immediate pair (left == right ? left), buta and b and aisAndf(Andf(a, b), a)and
those two are not equal. Collapsing it wants a dedup overAndf.LinearChildren, which is on
theInnerSimplifiedpath thatDomainConditionitself calls — so it needs a measurement
attached, not just a patch.Closing is yours; flagging it because a release is being prepared and this reads as an open
wrong-answer report while it is not one any more.Measured on
masterat9d9b21e4, .NET 10, default settings. Both halves of the title now answer correctly.log(1, 1).Evaled = NaN log(1, 1).Simplify() = NaN log(1, 2).Evaled = NaN log(-3, -3).Evaled = 1 log(-3, -3).DomainCondition = True log(x, x).Simplify() = 1 provided not x = 0 and not x = 1 log(b, x).DomainCondition = not b = 0 and not b = 1 and not x = 0log(1, 1)isNaNrather than0, andlog(-3, -3)'s domain condition isTruerather than a condition that holds nowhere — so the value1and the domain agree.The general condition is the one the issue argued for: a base of
0or1and an argument of0are excluded, and nothing else is.log(x, x)carries exactly those two exclusions rather than being answered1unconditionally.Closing as fixed. If any of the above is not what you read as the correct answer, reopening is cheap — the measurement is one probe.
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.