Wolfram Language conformance gaps¶
Known places where Woxi's output differs from wolframscript's.
Every entry was found by diffing the two engines and verified against the
Wolfram Language; none of them is fixed yet.
Where an entry says not reproducible, the difference comes from a choice Wolfram makes internally (an algorithm's tie-break, a library's rounding, a front-end representation) that cannot be recovered from its output — those are recorded so they are not investigated twice.
Output form, ordering and rendering¶
Woxi and Wolfram usually agree on the value and disagree on how it is printed. This is by far the largest group.
InputForm does not normalize function spellings to operator syntax¶
ToString[…, InputForm] (and plain output) prints the function-call spelling
where WL prints the operator, so the two spellings of the same expression
render differently:
# In a result (an unevaluated ReplaceAll is what a failed replacement returns):
wolframscript -code 'ToString[ReplaceAll[x, 5], InputForm]' # x /. 5
woxi eval 'ToString[ReplaceAll[x, 5], InputForm]' # ReplaceAll[x, 5]
# In a held expression:
wolframscript -code 'ToString[Unevaluated[Map[f, g]], InputForm]' # f /@ g
woxi eval 'ToString[Unevaluated[Map[f, g]], InputForm]' # Map[f, g]
The affected heads are the ones with operator spellings: ReplaceAll (/.),
ReplaceRepeated (//.), Map (/@), Apply (@@), and Part ([[…]]).
Part is the mildest of these — it renders correctly on its own and only
diverges inside a hold (ToString[Unevaluated[Part[a, 2]], InputForm]).
InputForm brackets more than the printed form does¶
wolframscript's ToString[…, InputForm] brackets things its top-level output
leaves bare, so the two disagree only when the InputForm string is compared:
wolframscript -code 'Hold[{1, 2}?f]' # Hold[{1, 2}?f]
wolframscript -code 'ToString[(Hold[{1, 2}?f]), InputForm]' # Hold[({1, 2})?f]
Woxi prints Hold[{1, 2}?f] in both. A Graph's options are the same story:
InputForm wraps them in a List (and reorders them into its own canonical
order), the printed form does not.
Unary minus inside a pure function round-trips as a subtraction¶
wolframscript -code 'ToString[Unevaluated[-# &], InputForm]' # -#1 &
woxi eval 'ToString[Unevaluated[-# &], InputForm]' # 0 - #1 &
Same family as the known Not[#2] vs !#2 rendering gap.
Association inside Unevaluated echoes with the wrong spelling¶
wolframscript -code 'ToString[Unevaluated[<|1 -> a|>], InputForm]' # Association[1 -> a]
woxi eval 'ToString[Unevaluated[<|1 -> a|>], InputForm]' # <|1 -> a|>
Cosmetic, and confined to held echoes: ToString[<|1 -> a|>, InputForm] agrees
on both engines.
-a/b groups differently, and a held reciprocal is not canonicalized¶
Woxi groups -a/b as -(a/b) where wolframscript groups it as (-a)/b — a
parse-precedence difference rather than a rendering one. Hold[-1/x] prints
-1/x where wolframscript canonicalises to -x^(-1). - -x and !!a are
parse errors in Woxi (the grammar forbids a second prefix operator).
ToString renders 1D where wolframscript renders 2D ASCII art¶
wolframscript -code 'ToString[a/b]' # a over a bar over b
woxi eval 'ToString[a/b]' # a/b
wolframscript -code 'ToString[x^2]' # a raised 2 above x
woxi eval 'ToString[x^2]' # x^2
The bare top-level echo is 1D in both engines — only ToString (and an
explicit OutputForm wrapper) triggers the 2D layout. Sqrt[x] and matrices
already agree. Switching the default ToString path to Woxi's existing 2D
renderer would move a great many snapshots at once, so fractions and powers are
still 1D.
Consequences elsewhere: ToString[-48/2033], ToString[-10/3],
ToString[1.5*^10] and 2D rationals inside message text all print flat.
NumberForm does not switch to scientific notation¶
wolframscript -code 'ToString[NumberForm[123456789.]]' # 1.23457 × 10^8
woxi eval 'ToString[NumberForm[123456789.]]' # 123457000.
Also NumberForm[1000000.] (WL 1. × 10^6) and NumberForm[1.5*10^-8]
(WL 1.5 × 10^-8). In-range reals — roughly 10^-5 ≤ |x| < 10^6 — agree.
NumberForm/ScientificForm round half-to-even, wolframscript rounds half-up¶
wolframscript -code 'ToString[ScientificForm[12345., 4]]' # 1.235 × 10^4
woxi eval 'ToString[ScientificForm[12345., 4]]' # 1.234 × 10^4
Only at an exact-half significant-figure boundary; anything with a non-zero
tail agrees. A latent edge in the same area: a decimal that rounds up across
10^6 (999999.6) stays 1000000. in the box path instead of switching to
1.×10^6.
NumberFormat and BaseForm of a real are unimplemented¶
NumberForm[…, NumberFormat -> f] stays unevaluated where WL renders
1234.5e, and BaseForm[0.5, 2] stays unevaluated where WL gives 0.1 over a
subscript 2.
TextString falls back to ToString for symbolic expressions¶
WL writes a FullForm-like linear form with numericized leaves — x + y →
"Plus[x, y]", Sqrt[x] → "Power[x, 0.5]", a/b →
"Times[a, Power[b, -1]]", but f[1/2] → "f[0.5]". Woxi keeps the ordinary
2D ToString form. (TextString[100!] is "∞" in wolframscript — a formatter
overflow that is deliberately not reproduced.)
TeXForm orders the terms of a sum by monomial degree¶
wolframscript -code 'ToString[TeXForm[1 + x + x^2]]' # x^2+x+1
woxi eval 'ToString[TeXForm[1 + x + x^2]]' # x+x^2+1
wolframscript -code 'ToString[TeXForm[a x^2 + b x + c]]' # a x^2+b x+c
woxi eval 'ToString[TeXForm[a x^2 + b x + c]]' # c+b x+a x^2
wolframscript -code 'ToString[TeXForm[Sin[x] + Cos[y]]]' # \sin (x)+\cos (y)
woxi eval 'ToString[TeXForm[Sin[x] + Cos[y]]]' # \cos (y)+\sin (x)
Woxi keeps the canonical order and only moves numbers (and complex atoms) to
the end; Wolfram sorts by the monomial, highest degree first. Neither
"reverse the canonical order" nor "stable sort by total degree" reproduces
all the samples — 3 + a + b stays a+b+3 (not reversed) while
Cos[y] + Sin[x] does reverse, and x^3 + x^2 y^2 keeps x^3 first even
though its total degree is lower — so this needs WL's actual lexicographic
monomial comparison. Everything else in a 211-expression TeXForm sweep
agrees.
TeXForm stacks a rational coefficient that wolframscript factors out¶
wolframscript -code 'ToString[TeXForm[(3 x^2 - 1)/2]]' # \frac{1}{2} \left(3 x^2-1\right)
woxi eval 'ToString[TeXForm[(3 x^2 - 1)/2]]' # \frac{3 x^2-1}{2}
Same for LegendreP[2, x], which evaluates to that expression in both
engines. A sum without a numeric term is stacked by both
((a + b)/2 → \frac{a+b}{2}, 3 (a + b)/2 → \frac{3 (a+b)}{2}), so the
trigger looks like a numeric term inside the numerator; the rule was not
pinned down.
x^(1/(2 y)) is a smaller instance of the same class: WL writes
x^{\left.\frac{1}{2}\right/y}, Woxi x^{\frac{1}{2 y}}.
TraditionalForm boxes are written out inline instead of as TemplateBoxes¶
ToBoxes[TraditionalForm[…]] differs in representation, not in picture:
wolframscript hands every special function to a named front-end template —
TemplateBox[{"n","x"}, "LegendreP"], {"x"}, "Gamma", {"s"}, "Zeta" —
while Woxi writes the same layout out as
SubscriptBox/SubsuperscriptBox rows, because it has no front end and its
own box renderers have to draw it. Only the outer
TagBox[FormBox[…, TraditionalForm], …] wrapper is common; n! and
Subscript[a, b] do conform.
Row is a template in both ({"2","x","t"}, "RowDefault") — but only in the
box escape an InputForm string carries, which nothing has to draw.
The box builder that feeds Woxi's own renderers keeps flattening it:
ToString[Row[{"a", 1}], TraditionalForm] is RowBox[{a, 1}] against WL's
TemplateBox[{"a", 1}, RowDefault], and ToBoxes[Row[{"a", 1}]] writes the
call's own source out as a RowBox. ToBoxes there also takes only one
argument where WL takes a form as the second.
Relatedly, expr_to_boxes typesets TraditionalForm with StandardForm glyphs:
Sin[x]/2 is FractionBox[RowBox[{Sin[, x, ]}], 2] against WL's
RowBox[{sin, (, x, )}], and Pi stays Pi instead of π.
ToBoxes in StandardForm drops StyleBox and held TagBox¶
ToBoxes[Style[x, Red]] is "x" where wolframscript gives
StyleBox["x", RGBColor[1, 0, 0], StripOnInput -> False]. The TraditionalForm
builder learned both; the StandardForm one has not.
A box segment mid-string is not shown as DisplayForm¶
WL renders a \!\(\*…\) box segment anywhere in a string as
DisplayForm[<box>] in terminal OutputForm; Woxi only does it when the string
starts with the segment. Generalising it would break plot labels, whose SVG
renderer typesets the raw markers — the real fix needs a "terminal text" vs
"text handed to a typesetter" distinction the form system does not have.
\!\(…\) without \* is full 2D linear syntax; WL parses it into a
RowBox, Woxi leaves it literal.
MessageName prints its long form in InputForm¶
OutputForm prints MessageName[f, usage] in both engines, but InputForm
prints the short f::usage in wolframscript and the long form in Woxi.
Machine reals in grid cells render at six significant figures¶
TableForm/Grid/MatrixForm cells and typeset labels round machine reals to
6 significant figures with a ×10^exp switch outside [-5, 5], matching WL's
StandardForm. The CLI eval InputForm keeps full precision, as
wolframscript -code does. This is a deliberate display rule, listed here
because it makes the two layers disagree with each other.
Canonical ordering of Plus and Times¶
Woxi has three independent canonical-order comparators (one for Sort, one for
Order, one for Plus terms) and they drift from each other as well as from
Wolfram. Diagnose an ordering divergence by testing Sort[…], Order[a, b]
and the bare a + b separately.
Plus orders a reciprocal monomial before a power of a sum¶
wolframscript -code 'ToString[1/x + Sqrt[1 - x^(-2)], InputForm]' # Sqrt[1 - x^(-2)] + x^(-1)
woxi eval 'ToString[1/x + Sqrt[1 - x^(-2)], InputForm]' # x^(-1) + Sqrt[1 - x^(-2)]
Only the reciprocal monomial diverges — x + Sqrt[1 - x^2] agrees, and the
Times counterpart of the rule is implemented.
Plus puts a composite term before a bare symbol¶
1 - n + Floor[k] in WL against 1 + Floor[k] - n in Woxi. Surfaces in
symbolic distribution CDFs with mixed symbol and Floor terms, e.g.
CDF[PascalDistribution[n, p], k].
A string atom sorts before a product in Plus¶
"m" + 2 a is m + 2*a in WL and 2*a + m in Woxi. For symbols Woxi orders
atom-versus-product correctly; strings live in a different ordering class that
skips the name comparison. Strings inside Plus never fully evaluate, so this
is pathological input — recorded only so the comparator is not touched for it.
x^2 + (a + b) x keeps Woxi's order¶
WL prints (a + b) x + x^2. The products-compare-from-the-last-factor rule is
implemented for Sort and Order but not for the Plus comparator, which is
the delicate one (it is not strictly transitive and its sort is wrapped in a
panic guard with a string-key fallback).
Nested sum-versus-sum factor order¶
(-1 + x)*(1 + (-2 + x)/2) — WL emits the more-nested factor first. Value
identical, display only. Surfaced through InterpolatingPolynomial's Newton
form.
Mixed sharing/non-sharing numerators over a shared denominator¶
(5 + x)/(3 + x) + b/(3 + x)^2 — WL puts the b term first, Woxi keeps the
sharing term first. Same for a*x/(1 - x) + b/(1 - x)^2. The all-sharing and
all-non-sharing cases are decoded and implemented.
c/x^2 + x^(-1) (a monomial rather than sum base) also diverges: WL puts
c/x^2 first.
Together does not hoist a numerator monomial for multi-factor denominators¶
wolframscript -code 'ToString[Together[x/(1-x) + x/(2-x)], InputForm]'
# -((x*(-3 + 2*x))/((-2 + x)*(-1 + x)))
woxi eval 'Together[x/(1-x) + x/(2-x)]'
# keeps the numerator expanded: (3*x - 2*x^2)/(...)
Related: after a cancellation Woxi returns an expanded numerator and
denominator where WL reconstructs from the surviving factors —
Together[(x^2 (1+x))/((1-x) x)] is -((x (1+x))/(-1+x)) in WL. And
Together[(x^2+x)/((x-1)(x+1))] stays put in Woxi where WL gives x/(-1+x).
Radical quotient canonicalization¶
WL folds integer coefficients into radicals of rationals and combines radical quotients where Woxi keeps them split:
wolframscript -code 'ToString[Numerator[Sqrt[11]/Sqrt[7]], InputForm]'
woxi eval 'Numerator[Sqrt[11]/Sqrt[7]]'
Concretely 3*Sqrt[11/3] → Sqrt[33], Sqrt[30]/Sqrt[6] → Sqrt[5], and
Numerator[-3 Sqrt[16]/(Sqrt[2] Sqrt[11])] is -6 Sqrt[2] in WL against
Woxi's -12. Woxi's Simplify is also non-confluent here: given the
already-distributed sum, Simplify[-6 Sqrt[399] + 6 Sqrt[2261]] returns
-6*(Sqrt[399] - Sqrt[2261]), while the product form it came from is fixed.
Sqrt[1/(2 Pi)] and constants outside a radical¶
Sqrt[1/(2 Pi)] stays as written instead of 1/Sqrt[2 Pi], and a constant
sitting outside the radical does not fold in: Pi/(2 Sqrt[2 Pi]) stays where
WL gives Sqrt[Pi/2]/2.
(1/2)^k is not rewritten to 2^(-k)¶
WL rewrites a positive unit-fraction base with a non-numeric exponent:
(1/2)^k → 2^(-k), (1/2)^Pi → 2^(-Pi), (1/2)^(k+1) → 2^(-1-k).
Woxi keeps the unit fraction. Non-unit numerators ((2/3)^k) and negative
bases ((-1/2)^k) agree.
Doing it eagerly regresses BinomialDistribution's PDF, which relies on
(1/2)^x*(1/2)^(10-x) merging to (1/2)^10; the rewrite has to happen after
same-base Times merging, or the integer-base merge has to combine
2^a*2^b → 2^(a+b) for symbolic exponents.
Woxi also normalizes (3/11)^(2/3) to (9/121)^(1/3), where WL keeps the
primitive base and even rewrites the other direction.
A complex coefficient blocks the negative-exponent fold¶
wolframscript -code 'ToString[TrigToExp[Sin[x]], InputForm]' # …I/2/E^(I*x)…
woxi eval 'ToString[TrigToExp[Sin[x]], InputForm]' # …I/2*E^(-I*x)…
Real, integer and symbolic coefficients all fold correctly
(1/2 E^(-I x) → 1/(2*E^(I*x))); only a complex or imaginary coefficient
fails. Coth and Csch are left unevaluated by TrigToExp for the related
reason that their denominators carry a negative real E-power, which Woxi
prints as -(1/E^x). ArcSin/ArcCos/ArcCsch/ArcSech are omitted because
the Log-argument Plus order diverges.
Expand leaves rational coefficients unfolded¶
wolframscript -code 'ToString[Expand[(x - 1/2)(x + 1/2)], InputForm]' # -1/4 + x^2
woxi eval 'Expand[(x - 1/2)(x + 1/2)]' # -1/4 - x/2 + x/2 + x^2
Within a term the folding is fixed; across terms it is not, because the
sum-level combiner keys Rational[1,2]*x and Rational[-1,2]*x differently.
Two smaller residues: Expand[-x/3] prints x*-1/3 where WL gives -1/3*x,
and Expand[(a-b)^2/12]'s middle term prints (2*a*b)/12 instead of
(a*b)/6.
Trig powers are not canonicalized to reciprocal functions¶
Wolfram rewrites 1/Sin[x] → Csc[x], Cos[x]/Sin[x] → Cot[x] and
Sin[x]^2/Cos[x] → Sin[x] Tan[x]. Woxi ships only the self-contained
reciprocal-pair subset (Sin[x] Csc[x] → 1 and friends); lone reciprocals and
the cross-function rewrites stay as written.
Simplify factors where wolframscript keeps an expanded quotient¶
Simplify[(-2 + x + 5x^2 + 2x^3)/(-5)] gives (-1/5)(1+x)(2+x)(-1+2x) in Woxi
against WL's (2 - x - 5x^2 - 2x^3)/5. Wolfram also factors numerators of
combined fractions further than Woxi does (collecting in x with factored
coefficients), so Woxi keeps a sum of quotients where WL prints one fraction —
Together alone matches exactly on the same inputs.
Simplify can be pathologically slow¶
Simplify[(z+1)*(1/(z+1) + PolyGamma[0,2] + (z+1)*(PolyGamma[0,2]^2 + PolyGamma[1,2]))/(z+1)]
takes tens of seconds where wolframscript is instant. The time is spent
re-evaluating the result tree after simplification completes, not inside
Simplify itself.
Three-factor sign flip¶
(-1/3)*(-2+x)*y diverges because Woxi evaluates BinaryOp pairs
left-associatively and flips the inner pair, while WL sees a flat Times.
Needs parser-level Times flattening.
Eliminate clears denominators differently¶
Eliminate[{x + y == 1, x - y == 2}, y] is x == 3/2 in Woxi and 2*x == 3
in WL, which keeps integer coefficients.
Numeric precision and floating point¶
Inexact zero loses WL's accuracy tracking¶
wolframscript -code 'ToString[PascalBinomial[6.0, -2], InputForm]' # 0``15.954589770191005
woxi eval 'PascalBinomial[6.0, -2]' # 0.
Binomial[6.0, -2] has the same reference value but drifts further — Woxi
returns an exact 0 there, losing the inexactness too. WL returns an
arbitrary-precision zero whose accuracy is $MachinePrecision — not a
machine 0., whose accuracy is 323.6. Reproducing it needs precision/accuracy
tracking through the Gamma-ratio path starting from a machine-real argument.
Machine floats overflow where wolframscript promotes to arbitrary precision¶
wolframscript -code 'Exp[1000] // N' # 1.97007111401704699388887935224`12.95…*^434
woxi eval 'Exp[1000] // N' # Infinity
wolframscript -code '1.0*^308 * 10' # 1.00000000000000001097906362944`15.95…*^309
woxi eval '1.0*^308 * 10' # Infinity
Anything that grows past ~1.8*^308 is affected, so Exp/Power of a few
hundred silently loses the answer rather than approximating it.
Positive and Sign underflow on a rational below the double range¶
wolframscript -code 'Positive[10^-400]' # True
woxi eval 'Positive[10^-400]' # False
wolframscript -code 'Sign[10^-400]' # 1
woxi eval 'Sign[10^-400]' # 0
Power and the comparison operators compare such values exactly, but
Positive, Negative and Sign still route through f64, where the value
underflows to 0.0.
An accuracy-form literal's precision differs in the last digit¶
1.5``20 stores its precision as 20 + log10(1.5). Woxi computes that with
f64::log10 and gets 20.17609125905568; wolframscript computes it at
arbitrary precision and gets 20.176091259055685. Visible whenever the
precision tag is printed. Accuracy[1.5] differs in the last ULP for the same
reason.
A symbolic constant raised to a BigFloat exponent stays symbolic¶
Pi^0.520` staysPi^0.520., so N[Gamma[1/2], 20] comes out malformed and
N[Gamma[3/2], 20] gives 0.520.Pi^0.520.. Routing it through
N[base, p] produces numbers but with a *different precision tag**
(20.30 against WL's 20.46, which accounts for the base magnitude) and a
last mantissa digit off by one — an arbitrary-precision rounding problem rather
than a dispatch one. N[Zeta[3], 25] is a separate gap: there is no
arbitrary-precision Zeta.
N[I] stays exact¶
N[I] is I in Woxi and 0. + 1.*I in wolframscript, so N[Gamma[I]] and
N[LogGamma[I]] echo instead of numericizing. Fixing it needs
scalar * (0. + c*I) to fold first, and that non-fold is deliberate: a pure
imaginary product is left as the c*I monomial so it still merges inside an
enclosing Plus. Both changes were tried and reverted.
BesselJ/PolyGamma at complex numeric arguments are separately unimplemented.
Complex[Real, Real] has the wrong head¶
Woxi has no dedicated Complex expression variant, so a real-real complex is
rewritten to Plus[Real, Times[Real, I]] during evaluation. The printed string
matches; the head does not.
Binomial[n, real] carries Gamma-error noise¶
Binomial[10, 3.] is 119.99999999999987 in Woxi and 120. in WL;
Binomial[100, 3.] is 161699.999999994 against 161700.0000000032. WL's
exactness is unpredictable — it depends on the magnitudes of the Gamma
arguments, not of the result, so Binomial[26, 13.] is f64-exact there and
Binomial[27, 13.] is not. Returning the exact BigInt fixes the small cases
and introduces new divergences wherever WL is itself inexact.
Not reproducible.
Variance and friends differ in the last ULP¶
Variance[{1., 2., 3.}] is 1. in Woxi and 1.0000000000000002 in WL, whose
accumulation matches none of the textbook formulas (Σ(x-m)²/(n-1),
(Σx² − n m²)/(n-1), (n Σx² − (Σx)²)/(n(n-1)), mean-of-squared-deviations
scaled, or Welford). Affects Variance, Correlation, Kurtosis, Cumulant
and Variance of an Audio. Not reproducible.
PageRankCentrality[CycleGraph[4]] is {0.25000000000000006, …} against WL's
clean {0.25, …} — power-iteration noise in the same class.
FresnelF and FresnelG lose accuracy through composition¶
Woxi composes them from FresnelS/FresnelC; wolframscript uses dedicated
rational approximations. Real outputs differ in the last 1–2 digits for small
|x|, and the cancellation as S, C → 1/2 degrades relative accuracy to
~1e-13 by x = 10. Not reproducible without WL's coefficients.
BesselI and BesselJ differ by 1–2 ULP¶
BesselI[0, 3] is …5025 in Woxi and …50235 in WL. BesselK and BesselY
are bit-exact. Affects e.g. PDF[VonMisesDistribution[2, 3], 2.5]'s last digit.
Also missing from Woxi's numeric folding: Gamma[rational], so
2. + Gamma[1/3] stays symbolic where WL gives 5.357877069415496.
Arbitrary-precision Root padding¶
N[Root[#^3 - # - 1 &, 1], 10] prints ~40 digits in both engines, but only the
first ~17 are the ones that were asked for; past that the two continuations
disagree. Woxi's is the correct one (checked against the plastic constant to 40
digits) — wolframscript pads with digits of its own. Same for
N[Root[#^4 - # - 1 &, 1], 15].
MidDate on sub-second instants¶
Woxi carries an instant as one f64 count of seconds since the epoch, whose
resolution near 1.7e9 is ~2.4e-7 s, so averaging instants that carry fractional
seconds differs from wolframscript's exact mean in the 8th decimal. A midpoint
that lands on a whole second is reported exactly, as an integer, matching
wolframscript.
Image filters accumulate in a different width¶
RecurrenceFilter and Sharpen on an Image differ from wolframscript by one
Real32 ULP in single taps (0.7749999761581421 against 0.7750000357627869):
Woxi accumulates in f64 and snaps the result to the stored Real32 pixel,
wolframscript accumulates in Real32 throughout. Not reproducible without
matching its accumulation order.
N does not push into Cos[real*Pi]¶
N[Cos[Pi/28], 8] leaves Cos[0.0357*Pi] unevaluated.
NIntegrate, NSum and NProduct accuracy¶
NIntegrate never matches wolframscript digit for digit on any input — test
against the exact value with a tolerance, not against WL. Specific gaps:
NIntegrate[x^(-0.99), {x, 0, 1}]gives30.86for 100. The tail decays likedistance^0.01, past what double precision can represent; wolframscript does not converge either (it warns::ncvband reports100.0036). Not reproducible.NIntegrate[1/Sqrt[x], {x, 0, 1}]is off by 1.8e-5.NProducton a slowly convergent infinite product is ~2e-6 off where WL is ~3e-11 (NProduct[1 + 1/n^2, {n, 1, Infinity}]→ 3.676076175562187 against 3.6760779100585657). Finite products are exact.NSumis more accurate than WL.
Numeric optimization lands on different points¶
Wolfram's interior-point method answers FindMinimum[{x^2, x >= 1}, x] with
1.000000013282579 where Woxi returns the exact optimum, and
NArgMax[{x + y, x^2 + y^2 <= 1}, {x, y}] with 0.7071076183816036. A
three-variable equality constraint lands within 1.5e-8 in Woxi rather than
exactly. Neither engine is wrong; bit-equality is not achievable.
FindRoot options and damping schedule¶
WorkingPrecision, AccuracyGoal and PrecisionGoal are ignored. WL's answer
under WorkingPrecision carries iteration residue past the requested
precision, so matching it needs its exact iteration and rounding schedule. Its
damping schedule for an oscillating Newton also differs
(FindRoot[x^3 - 2x + 2, {x, 0}, MaxIterations -> 4] is 0.816037292480469
in WL, 0.8142908074786004 here), and the three-argument secant form differs
in the last digit.
Cross-platform libm differences¶
The last ULP of atanh, acos, asinh and friends differs between macOS and
Linux, so a full-precision string assertion is platform-dependent. The same
1-ULP FMA difference flips a single 8-bit colour channel in ComplexPlot
domain-colouring output at an exact x.5 boundary.
Algebra and calculus¶
Integrate[Log[Sin[x]], …] is unimplemented¶
wolframscript -code 'ToString[Integrate[Log[Sin[x]], {x, 0, 1}], InputForm]'
# (-1/12*I)*(6 + (-6 + Pi)*Pi - (12*I)*Log[2] - 6*PolyLog[2, E^(2*I)])
woxi eval 'Integrate[Log[Sin[x]], {x, 0, 1}]'
# Integrate[Log[Sin[x]], {x, 0, 1}]
The antiderivatives are known:
Integrate[Log[Sin[x]], x] ==
-(x*Log[1 - E^((2*I)*x)]) + x*Log[Sin[x]] + (I/2)*(x^2 + PolyLog[2, E^((2*I)*x)])
Integrate[Log[Cos[x]], x] ==
(I/2)*x^2 - x*Log[1 + E^((2*I)*x)] + x*Log[Cos[x]] + (I/2)*PolyLog[2, -E^((2*I)*x)]
The limit at x -> 0 contributes I Pi^2/12. The blocker is not the value but
the printed form: WL's answer is Simplify-collected, and Woxi does not land
on that grouping.
Integrate[Sqrt[a ± x^2], x] stays unevaluated¶
The antiderivative exists in Woxi already (the definite path uses it), and the
values verify — but the printer reorders x*Sqrt[1-x^2] to Sqrt[1-x^2]*x
whenever the radical contains the integration variable, and the Plus term
order flips too. WL keeps (x*Sqrt[1-x^2] + ArcSin[x])/2.
Irreducible-quartic rational integrals¶
Integrate[1/(1+x^4), x] and its family stay unevaluated: the integrator does
not partial-fraction over quadratics, and WL's output uses ArcTan-reflection
forms (ArcTan[2/x] against ArcTan[x/2]) that would diverge anyway.
Also still unevaluated: Integrate[x Log[x]^2, x], and
Integrate[Log[3 x], {x, 1, 2}] gives -1 - Log[3] + 2 Log[6] where WL
combines to -1 + Log[12].
Integrate under assumptions¶
Integrate[Exp[-x t], {t, 0, Infinity}, Assumptions -> x > 0] gives
x^(-1) - 1/(E^(Infinity*x)*x) instead of 1/x — the boundary is an infinite
limit, not a refinement target. Without assumptions
Integrate[x^n, {x, 0, 1}] is (1+n)^(-1) - 0^(1+n)/(1+n) where WL emits
ConditionalExpression[(1+n)^-1, Re[n] > -1], a form Woxi does not build.
Sin[a x]/x and one-sided 1/x^p improper integrals stay unevaluated.
The Gaussian Integrate[Exp[-a x^2], {x, -Infinity, Infinity}] gives
Sqrt[Pi/a] against WL's Sqrt[Pi]/Sqrt[a].
LaplaceTransform[t^n f(t), t, s] is unimplemented¶
The rule L[t^n f] = (-1)^n d^n/ds^n L[f] is straightforward and the values
come out right, but WL canonicalizes an even power of a binomial by flipping
the base sign — (s-a)^(-2) prints as (a-s)^(-2) — keeps (-1+s^2)^2 where
Woxi factors, and leaves an un-combined sum after the derivative. Plain
Sin/Cos/Exp/t^n transforms all match, as does the s-shifting theorem
L[E^(c t) g(t)]; Cosh/Sinh integrands are excluded from the shift because
WL canonicalizes the resulting difference-of-squares denominator inconsistently
(factored for Cosh, expanded for Sinh).
InverseLaplaceTransform of a complex-pole rational function¶
A proper rational function with exact coefficients is inverted exactly, off
its partial-fraction decomposition, and the residue sum is regrouped as
E^(k_min t) times a polynomial in E^t — which reproduces WL for real
poles (1/((s+1)(s+2)(s+3)) → (-1 + E^t)^2/(2 E^(3 t)), repeated poles
included). A complex-conjugate pole pair diverges in form only: Woxi
returns the real damped oscillation, WL a sum of complex exponentials.
wolframscript -code 'InverseLaplaceTransform[1/(s^3 + 2 s^2 + 5 s), s, t]'
# 1/5 + ((I/20)*((-1 + 2*I) + (1 + 2*I)*E^((4*I)*t)))/E^((1 + 2*I)*t)
woxi eval 'InverseLaplaceTransform[1/(s^3 + 2 s^2 + 5 s), s, t]'
# -1/10*(-2*E^t + 2*Cos[2*t] + Sin[2*t])/E^t
Both are the same function (1/5 - E^-t (Cos[2t]/5 + Sin[2t]/10)). WL's
choice is its Simplify acting on complex residues, and it is not
consistent — (s+2)/((s+1)(s^2+4)) comes back in the real Cos/Sin
form, 1/(s^2+2 s+5) in the complex-exponential one. Simplify on the
same input agrees with WL, so the divergence is in what each side feeds it.
A real-pole numerator can also land on a different-but-tied form:
s/((s+1)(s+2)(s+3)) factors the polynomial in E^t where WL keeps it
expanded (both cost the same by WL's own SimplifyCount).
An improper fraction's DiracDelta derivative term also sorts differently
inside the Plus: InverseLaplaceTransform[s^2/(s+1), s, t] is
Derivative[1][DiracDelta][t] + E^(-t) - DiracDelta[t] where WL puts the
Derivative term last. Same terms, canonical-order divergence for a
curried head.
Fourier*Series are unimplemented¶
FourierSinSeries, FourierCosSeries, FourierTrigSeries and FourierSeries
echo. The coefficients are easy; WL's output form is unpredictably factored —
FourierTrigSeries[x, x, 3] is expanded, FourierTrigSeries[x^2, x, 2] is
factored as Pi^2/3 + 4*(-Cos[x] + Cos[2*x]/4), and the same expression
factors differently across the Sin/Cos/Trig variants.
Factor with GaussianIntegers, Extension or Trig¶
Recognized as valid options but the call stays unevaluated; WL factors
x^2+1 into (-I + x)*(I + x). Same for FactorList and
IrreduciblePolynomialQ. Composite-modulus LinearSolve, n-ary modular
PolynomialLCM and composite-modulus GroebnerBasis are also left
unevaluated (prime moduli work).
PolynomialGCD is wrong for multivariate input¶
wolframscript -code 'PolynomialGCD[x*y, y]' # y
woxi eval 'PolynomialGCD[x*y, y]' # 1
wolframscript -code 'PolynomialGCD[2 x y, x^2]' # x
woxi eval 'PolynomialGCD[2 x y, x^2]' # 2*x*y
The third argument of the 3-argument form is effectively ignored. Multivariate
GCD needs a real algorithm (subresultant PRS over a polynomial ring, or a
modular method); the current implementation is univariate only. This blocks
multivariate SquareFreeQ, which stays unevaluated for the same reason.
Apart residues¶
Two shapes remain: a missing polynomial division when the leading denominator
coefficient is negative (1/4 + (9+17x+23x^2)/(4*(3-5x-3x^2+4x^3))), and a
0 + expr artifact when the polynomial quotient is zero.
PartialFractions is unimplemented¶
WL's PartialFractions[expr, x] (unlike Apart) always splits to linear
factors over the algebraic closure, so an irreducible quadratic produces
(-1)^(1/3), Sqrt and three-argument Root coefficient forms. Only the
rational-factorable case coincides with Apart. Matching the coefficient forms
needs the roots-of-unity and Root display pipeline.
Series[x!, {x, 0, n}] coefficient form is order dependent¶
The x^2 coefficient is WL-canonical (6*EulerGamma^2 + Pi^2)/12 when it is
the last one and re-folds to (EulerGamma^2 + Pi^2/6)/2 once it becomes
interior; the x^3 coefficient diverges similarly. Value correct, form only.
Symbolic Sum is limited to monomials from 1¶
Non-unit lower bounds, linearity (Sum[2k, …], Sum[k^2+k, …]) and an
explicit step with a symbolic bound are all unevaluated or refused. A general
Faulhaber engine is mathematically straightforward and was written, but its
output form does not match: Sum[k+1, {k, 1, n}] is (3*n + n^2)/2 in WL,
while Woxi's Simplify gives (n*(3+n))/2, and factor ordering diverges
((1+Floor[n/2])*Floor[n/2]).
Clean finite-sum targets still unevaluated:
Sum[k Binomial[n,k], {k,0,n}] = 2^(-1+n) n,
Sum[k^2 Binomial[n,k], …] = 2^(-2+n) n (1+n),
Sum[k 2^k, {k,1,n}] = 2(1-2^n+2^n n), and Sum[Cos[k x], {k,1,n}].
Sum[(-1)^(2n)/n^2] also fails, because (-1)^(2n) is not simplified to 1.
Infinite-sum gaps¶
- Rational sums whose residues do not cancel per class have transcendental
values Woxi cannot produce:
Sum[1/(9n^2-1)]=(9 - Sqrt[3] Pi)/18,Sum[1/(n(2n-1))]=Log[4]. NeedsPolyGammaat rational arguments. PolyLogsums withn^k, k ≥ 2:Sum[1/(2^k k^2)]=Pi^2/12 - Log[2]^2/2.- Divergence messages:
Sum[2^n/n]andSum[1/(n Log[n])]are silent in Woxi where WL reports. WL's own boundary is inconsistent here (bare2^nsilent,2^n/nmessaged). Product[R(n), {n, n0, Infinity}]handles integer roots only; rational roots (Product[1-1/(4n^2)]=2/Pi) would needGammaat half-integers.
Sum/Product unevaluated echoes keep the source order¶
Product[-n, …] echoes as 0 - n (a parse artifact) and Woxi keeps the source
Plus order (n+2) where WL canonicalizes the held body (2+n).
Limit returns wrong values for factorial growth¶
wolframscript -code 'Limit[n!^(1/n), n -> Infinity]' # Infinity
woxi eval 'Limit[n!^(1/n), n -> Infinity]' # 1
wolframscript -code 'Limit[n!^(1/n)/n, n -> Infinity]' # 1/E
woxi eval 'Limit[n!^(1/n)/n, n -> Infinity]' # 0
wolframscript -code 'Limit[Log[n!]/n, n -> Infinity]' # Infinity
woxi eval 'Limit[Log[n!]/n, n -> Infinity]' # 0
Also Limit[LogGamma[n]/n] (WL Infinity, Woxi 0) and Limit[(2n)!/n!]
(WL Infinity, Woxi 1). The engine applies a base^(1/n) → 1 /
Log[g]/n → 0 shortcut that only holds for polynomial growth. This is the one
place in Limit that returns a wrong value rather than an unevaluated
call — everything else below only fails to evaluate.
Three Limit shapes at infinity stay unevaluated¶
wolframscript -code 'Limit[x^100/Exp[x], x -> Infinity]' # 0
woxi eval 'Limit[x^100/Exp[x], x -> Infinity]' # Limit[x^100/E^x, x -> Infinity]
wolframscript -code 'Limit[Log[Log[x]]/Log[x], x -> Infinity]' # 0
woxi eval 'Limit[Log[Log[x]]/Log[x], x -> Infinity]' # Limit[…] unevaluated
wolframscript -code 'Limit[x/(x + Sqrt[x]), x -> -Infinity]' # 1
woxi eval 'Limit[x/(x + Sqrt[x]), x -> -Infinity]' # Limit[…] unevaluated
Each has its own cause. A polynomial over an exponential resolves up to about
degree 45 and then hits the L'Hôpital depth guard. Nested logarithms need a
u = Log[x] substitution — Limit[Log[u]/u, u -> Infinity] is already 0.
The -Infinity case is excluded on purpose: the leading-order analysis is
gated to +Infinity because x^p for non-integer p is not real to the
left of zero, and extending it needs branch-cut care.
Consequences: AsymptoticGreater[Exp[x], x^100, …] and
AsymptoticLess[Log[Log[x]], Log[x], …] stay unevaluated too.
Other open limits: Limit[Sin[1/x], x -> 0] should be Indeterminate,
Limit[HarmonicNumber[n]/Log[n]] should be 1, and
Limit[HarmonicNumber[2n] - HarmonicNumber[n]] should be Log[2].
Limit[Binomial[2n,n], n -> Infinity], Limit[1/Binomial[2n,n]] and
Limit[Binomial[2n,n]^(1/n)] hang (the numeric probe evaluates Binomial
at n = 10^6), as does the literal-Sum spelling of the harmonic-minus-log
limit.
Residue and Simplify around Gamma poles¶
A Gamma pole model multiplied by another pole at a non-zero point —
Residue[Gamma[z]/(z+1), {z, -1}], WL -1 + EulerGamma — is refused rather
than risk the Simplify slowness above.
RSolve first-order linear inhomogeneous¶
RSolve[a[n] == c a[n-1] + d && a[k0] == v0, a[n], n] with constant c ≠ 1
returns unevaluated. The closed form is trivial; WL's display varies
case by case through power folding, with no clean subset:
| recurrence | wolframscript |
|---|---|
| c=2, d=1, a[0]=2 | -1 + 3*2^n |
| c=2, d=1, a[0]=3 | -1 + 2^(2 + n) (the coefficient folded into the exponent) |
| c=2, d=1, a[0]=4 | -1 + 5*2^n |
| c=2, d=5, a[0]=0 | 5*(-1 + 2^n) (common factor pulled out) |
| c=3, d=2, a[0]=5 | -1 + 2*3^(1 + n) (partial fold) |
| c=5, d=3, a[0]=10 | (-3 + 43*5^n)/4 |
RSolve of the logistic map at r=2 is deliberately left unevaluated for the
same reason: WL displays an internally rewritten rational form
((-2^2^(1+n) + 5^2^n)/(2*5^2^n)) rather than the clean (1 - (1-2c)^2^n)/2.
Casoratian is unimplemented¶
The semantics are fully decoded, but every form needs exponential collapse
Woxi's Simplify lacks ({2^n, 3^n} → 6^n, {n!, 2^n} → a Gamma form,
{Sin[n], Cos[n]} → -Sin[1]), and WL is internally inconsistent
({2^n,3^n,5^n} → 5^n*6^(1+n), not 6*30^n). Only the first-order-system
form (Det[A]^n) is form-stable.
FindGeneratingFunction is unimplemented¶
Not a clean rational fit: {1,1,1,1,1} gives a Padé artifact, {1×7} and
{1×10} give different families of answer, and the display switches between
raw power and Together form. Not reproducible.
MultipleZeta reduction¶
WL reduces multiple zeta values through at least weight 8 using
MultipleZeta[{5,3}] as a basis element. Depth 1, leading 1 and the empty
argument are easy; matching the reduction extent needs full MZV datamine
tables.
Higher-rank Curl¶
Scalar in 3D, vector in 4D and rank-2 tensor in 3D — the valid higher-rank
antisymmetric forms — stay unevaluated. WL returns them as SymmetrizedArray
(or a collapsed 0). Needs exterior derivative plus Hodge dual.
Second-order PDEs with a lower-order term¶
DSolve[u_xx + u_y == 0] (the heat equation) is unevaluated; WL falls back to
a particular solution with C[1] … C[8] after DSolve::lpdeprtclr. With
a == 0 or c == 0 WL also writes the characteristics unnormalised
(C[1][x - y] + C[2][x]) rather than as λ x + y.
NDSolve covers ODEs only¶
NDSolve[{D[u[x,t],t] == D[u[x,t],x,x], u[x,0] == Sin[Pi x],
u[0,t] == 0, u[1,t] == 0}, u, {x,0,1}, {t,0,1}]
returns unevaluated. NeumannValue is out of scope, DirichletCondition
exists only as a symbol, and Method -> {"MethodOfLines", …} has nothing
behind it. On the symbolic side DSolve recognises three first-order
two-variable PDE shapes; Laplace, which WL solves as
C[1][I x + y] + C[2][-I x + y], is not among them.
NDSolve's DAE support handles an index-1 constraint that solves explicitly
for one unknown; quadratic, coupled or index ≥ 2 constraints, and constraints
whose unknown is also differentiated, are not covered.
NDSolve uses a fixed grid¶
Woxi integrates on 1000 nominal RK4 steps with bisection refinement;
wolframscript adapts. Length[(y /. s[[1]])[[2]]] is 1001 against 13. Note
that stock wolframscript's default tolerances can be less accurate than
Woxi's here — get a converged reference before treating a difference as a bug.
Reduce does not eliminate quantifiers¶
wolframscript -code 'Reduce[Exists[y, x == y^2], x, Reals]' # x >= 0
woxi eval 'Reduce[Exists[y, x == y^2], x, Reals]' # Reduce[Exists[y, x == y^2], x]
wolframscript -code 'Reduce[ForAll[y, x + y^2 >= x], x, Reals]' # True
woxi eval 'Reduce[ForAll[y, x + y^2 >= x], x, Reals]' # Reduce[ForAll[y, x + y^2 >= x], x]
Exists and ForAll are parsed but never eliminated. Note the unevaluated
form also drops the Reals domain argument, which is a separate bug in the
echo path.
Other Reduce and FindInstance divergences¶
- Complex-domain
Absis a wrong answer:Reduce[Abs[x] < 3, x]returnsFalsealthough solutions exist. WL gives aRe/Imdecomposition, since the default domain isComplexes. - Multi-symbol parametric coefficients:
Reduce[a b x == c, x]gives an incompletex == c/(a*b)where WL enumerates three sub-cases. The single-symbol case is implemented, and only for the default domain. - The two-argument
x ∈ Integersmembership form does not enumerate; the three-argumentReduce[…, x, Integers]form does. - Disjunct ordering:
Reduce[x > 0 || x < -1, x]— WL sorts the regions, Woxi keeps input order. Reduce[Sin[x] == 1, x]— WL puts theElementcondition as a top-levelAnd, Woxi wraps it inConditionalExpression.Reduce[…, Modulus -> n]ignores the modulus for a multivariate nonlinear system (univariate and linear systems work).Solverefuses that case rather than answer wrongly.FindInstancepicks a different, still-valid instance:x^2 == 2givesSqrt[2]against WL's-Sqrt[2];x^2 + y^2 == 1over the reals gives{x -> -1, y -> 0}against{x -> 1, y -> 0};x^2 > 4gives-100against-4. Not reproducible.FindInstanceover the integers falls back to a bounded search whenSolvecannot decide: every variable is walked outwards from zero (0, 1, −1, 2, −2, …) within a fixed evaluation budget. That reproduces wolframscript's answer for the small instances (x^5 + y^5 + z^5 == w^5 && x > 0→{1, 0, 0, 1}) but cannot reach a large one — Euler's sum-of-powers counterexample27^5 + 84^5 + 110^5 + 133^5 == 144^5stays unevaluated where wolframscript finds it.
Solve over a system of two Abs equations gives no solutions¶
wolframscript -code 'Solve[{Abs[x] == 2, Abs[y] == 3}, {x, y}]'
# {{x -> -2, y -> -3}, {x -> 2, y -> -3}, {x -> -2, y -> 3}, {x -> 2, y -> 3}}
woxi eval 'Solve[{Abs[x] == 2, Abs[y] == 3}, {x, y}]'
# {}
One Abs equation is solved correctly, and a single Abs equation narrowed by
an inequality is too. It is the multi-variable elimination that cannot take two
of them apart and reports the system unsatisfiable, so ToRules turns False
into an empty list.
Solve orders the negative root first in a two-variable system¶
wolframscript -code 'Solve[{x^2 + y^2 == 1, y == 0}, {x, y}]'
# {{x -> 1, y -> 0}, {x -> -1, y -> 0}}
woxi eval 'Solve[{x^2 + y^2 == 1, y == 0}, {x, y}]'
# {{x -> -1, y -> 0}, {x -> 1, y -> 0}}
The single-variable case agrees (Solve[x^2 == 4, x] is {{x -> -2}, {x -> 2}}
in both), so the system path sorts where wolframscript does not.
Solve over the integers drops a range constraint¶
wolframscript -code 'Solve[Mod[x, 3] == 1 && 0 <= x < 10, x, Integers]'
# {{x -> 1}, {x -> 4}, {x -> 7}}
woxi eval 'Solve[Mod[x, 3] == 1 && 0 <= x < 10, x, Integers]'
# Solve[Mod[x, 3] == 1, x]
Bounded linear systems over the integers are already enumerated; a Mod
congruence with an explicit range is the same shape. The returned expression
having lost both the bound and the domain makes this look worse than a plain
unevaluated result.
Roots root ordering¶
Roots[x^2 - 3x + 2 == 0, x] is x == 2 || x == 1 in Woxi and
x == 1 || x == 2 in WL. But WL's order is not a clean sort — x^2-9 gives
3, -3, x^4-5x^2+4 gives -2,-1,1,2, x^2-2 gives Sqrt[2], -Sqrt[2],
while x^2-2x-1 keeps Solve's order. It depends on which internal
root-isolation path the degree takes. Woxi's Solve ordering does match
exactly; only Roots diverges. Not reproducible.
Related and still open: Roots[x^n == c, x] uses ω^k generation order starting
at k=1 when the radicand is not a perfect power, and Root of a reducible
polynomial with cyclotomic factors prints a+bI (Root[x^6-1, 3] =
(-1 - I Sqrt[3])/2) where Woxi keeps the Solve form -(-1)^(1/3).
Inexact polynomial solving¶
With any inexact coefficient WL's Solve is NSolve — purely numeric, no
exact factoring, so Solve[x^3 - 1.5 x^2 + x - 1.5 == 0, x] gives
1.4999999999999996 rather than 3/2. Woxi agrees on that model, and its
Durand–Kerner roots agree to 1–2 ULP — but never assert full digits. The one
case WL still gets exactly right is a triple root
(x^3 - 2.25 x^2 + 1.6875 x - 0.421875 → {0.75, 0.75, 0.75}), which it
resolves at raised internal precision; f64 Durand–Kerner cannot.
Minimize does not detect an unattained infimum¶
Minimize[1/(1+x^2), x] returns a bogus large-x numeric instead of WL's
natt message and 0.
Symbolic Fit and LinearModelFit¶
LinearModelFit[…]["BestFit"] drops the 0. + constant term wolframscript
keeps, and Fit[{1,2,3,4},{1,x},x] gives a clean 0. + 1. x where WL keeps
5.07*^-16 + 0.9999999999999999 x — Woxi is the less noisy one.
For a rank-deficient design wolframscript is not minimum-norm:
Fit[{{1,1},{2,2},{3,3}}, {1, x, x^2, x^3}, x] gives coefficients of norm
0.7546 where the minimum-norm solution has norm 0.6134. Both interpolate;
WL switched to some other regularization there. Not reproducible.
Constrained and weighted fitting find different local minima¶
On a non-convex constrained problem the two engines land on different valid local minima — for a sine fit Wolfram lands on the constraint boundary while Woxi finds an interior point with a smaller residual. Compare the picture, not the parameters.
PowerMod[a, 1/n, m] with multiple roots¶
When gcd(a,m)=1 and gcd(n, λ(m)) ≠ 1 with n ≥ 3, WL returns a specific root
whose choice depends on per-prime CRT lifting and follows no
smallest/largest rule — n=4 gives the largest for two probes and the smallest
for a third. Woxi leaves those unevaluated rather than return a possibly-wrong
root. The unique-root and no-root regimes match exactly, as does n=2.
Not reproducible.
Boolean minimization¶
BooleanConvert ships every wolframscript form except "ESOP" and "BFF",
but four differences remain:
- DNF needs term merging, not just redundancy removal:
(a&&!b)||(!a&&b)||(a&&b)isa || bin WL; Woxi keeps three terms. - CNF clause strengthening:
(!a||b||c)&&(a||!b)&&(b||!c)— WL shortens the first clause to(!a||b). - Clause order when clause lengths differ:
Xor[a, Implies[b,c]]— neither variables-first nor length-first reproduces WL together with(a||b)&&c. - Equivalent-but-different CNF choices:
a&&b&&c||!a&&!b&&!c— WL keys on(a,b)/(b,c)pairs, Woxi on(a,c)/(b,c). WL also sometimes produces a resolution-redundant CNF and a non-minimal DNF cover that Woxi does not.
BooleanConvert[Nand[a,b,c], "ANF"] prints Nand[a, b, c] in WL (it does not
rewrite the head) against Woxi's !(a && b && c), and
BooleanConvert[Equivalent[Xor[a,b],c], "IF"] branches on c first, so WL's
BDD variable order is not always alphabetical. BooleanFunction[n, k] and
BooleanMinterms/BooleanMaxterms with an integer variable count normalise to
BooleanFunction["BDD" -> …] in WL; Woxi leaves them alone, and
BooleanMinterms[{1,2}, 2] wrongly emits ::bspec.
SatisfiabilityInstances ordering¶
The default single instance (greedy True-first DFS) matches, and an
unsatisfiable problem gives {}. The multi-instance and All ordering
follows WL's internal BDD structure and differs per expression —
a||b||c orders 7,3,1,5,2,6,4 while Majority[a,b,c] orders 7,6,5,3.
Not reproducible.
Special functions¶
HypergeometricPFQ residues¶
HypergeometricPFQ[{}, {b}, z] with a negative half-integer b below -5/2
diverges in shape: WL stops collecting and prints a nested Bessel form. Two
smaller divergences live in the core Power normalizer and reproduce without
any hypergeometric: (64/729)^(1/4) stays put where WL gives (2 Sqrt[2/3])/3,
and Woxi's prefactor prints 4^(1/2 - b/2) where WL prints 2^(1 - b).
Hypergeometric1F1[a, b, z] with symbolic z and 1 ≤ a < b:
HypergeometricPFQ[{1},{2},x] is (-1+E^z)/z in WL and unevaluated in Woxi.
The a=1 family has a clean formula, but Woxi orders E^z after the z-power
terms where WL puts it right after the constant.
Carlson elliptic integrals¶
CarlsonRF[0, y, z] with distinct non-zero arguments needs CarlsonRK, and
symbolic CarlsonRC[y, y] needs a Piecewise — both unimplemented, so Woxi
echoes. CarlsonRK and CarlsonRM are unimplemented outright.
InverseWeierstrassP is broken: InverseWeierstrassP[0.5, {1,2}] returns a
garbage real pair {-109.5, -39.3} where WL returns a scalar complex
1.308 + 0.469 I. The correct route is
℘⁻¹(p) = CarlsonRF[p-e1, p-e2, p-e3], but WL's representative selection is not
a simple sign or conjugate of it — for one probe it is the conjugate, for
another it differs by a lattice period. Picking it needs the period lattice.
Weierstrass and elliptic gaps¶
WeierstrassHalfPeriods covers only the real positive-discriminant case; the
rhombic case needs a complex EllipticK. WeierstrassInvariants[{1,I}] and
WeierstrassHalfPeriods[{4,0}] with exact arguments give Gamma closed forms in
WL and stay symbolic in Woxi. WeierstrassZeta, WeierstrassSigma,
SiegelTheta and QPolyGamma are unimplemented.
Neville theta values are 1–2 ULP off wolframscript on some points.
Mathieu functions¶
MathieuC[1, 2, 0.5] is 0.26 + 1.17 I in WL — the Mathieu functions are
complex for a general characteristic value. Woxi's MathieuS returns a wrong
real value, having confused the order-versus-characteristic convention.
A correct fix needs a full Floquet implementation (continued fractions for the
characteristic exponent plus Fourier coefficients).
SphericalHarmonicY numericizes exact angles¶
SphericalHarmonicY[l, m, Pi/2, 0] returns a machine float (e.g. 2.99…*^-17
instead of an exact 0) where wolframscript returns the exact symbolic form.
Two residual form divergences remain even on the symbolic path:
Sqrt[Pi^(-1)] against 1/Sqrt[Pi], and E^(I k Pi) against (-1)^k.
LaguerreL with a fractional second argument¶
Value correct, form divergent: (105/8 - (105*x)/4 + (21*x^2)/2 - x^3)/6 in
Woxi against (105 - 210*x + 84*x^2 - 8*x^3)/48 in WL. WL's evaluator does not
hoist content out of Times[1/6, Plus[…]] when typed directly, so LaguerreL
must build the common denominator internally — but LaguerreL[1, 1/2, x] stays
3/2 - x rather than (3 - 2*x)/2, so it is not a plain Together either.
Integer second arguments match.
ArcCot[Interval[…]] is unevaluated¶
WL converts endpoints to ArcTan form inside the interval (ArcCot[2] →
ArcTan[1/2]), which differs from its own scalar ArcCot[2].
WignerD[{1,0,0}, Pi/2]¶
Gives 2.22e-16 instead of an exact 0.
LerchPhi outside the unit disc¶
The numeric path sums Σ z^k/(k+a)^s and converges only for |z| < 1 (plus a
tail for z = 1, s > 1); outside that region the call is unevaluated.
wolframscript continues analytically —
LerchPhi[2, 3, -1.5] → 51.981861922538684 - 2.1345964981239467*I. A correct
implementation needs Crandall's algorithm or a contour formula.
SyntaxLength is unimplemented¶
wolframscript -code 'SyntaxLength["1+"]' # 4
woxi eval 'SyntaxLength["1+"]' # SyntaxLength[1+] (+ "not yet implemented" warning)
SyntaxLength[s] is the length of the longest prefix of s that could still
begin a complete expression — 4 for "1+" (WL counts past the end of the
string, since more input could complete it).
Capitalize[str, "TitleCase"] is unimplemented¶
The algorithm is verified (tokenize on whitespace, first and last words
capitalized, middle "small words" lowercased), but the small-word set is WL's
complete internal preposition/conjunction/article list, keyed on the literal
word. 130+ candidates were tested and it kept growing — common (a, the, of),
participial (considering, barring, notwithstanding, but not
excluding/following), and archaic (betwixt, qua, ere, sans, cum, nigh, outwith,
thru, contra, vice). Any finite list diverges on untested words.
Not reproducible. SpokenString and TextCases entity extraction are the
same class.
TextSentences grouping¶
WL's segmenter groups short sentences oddly ("One. Two. Three." →
{"One.", "Two. Three."}) while handling "Dr. Smith went home." correctly.
Woxi's straightforward splitting is the sensible answer. Not reproducible.
Hash[expr] for non-strings¶
wolframscript hashes an internal serialisation: Hash[1, "CRC32"] is
3017272578 and Hash[{1,2}, "CRC32"] is 1789268987, matching CRC32 of neither
the WXF, the InputForm text, the FullForm text, nor an integer encoding.
Strings and ByteArrays are special-cased to raw bytes and do match, as do
all the named algorithms on them. Hash[expr] with no type is WL's proprietary
64-bit expression hash. Compress output length differs too (their container
format). Not reproducible.
Distributions and statistics¶
Most distribution divergences are the Plus/Times ordering and Sqrt-split
classes above, reached through a moment formula. The recurring shapes:
Sqrtproduct split —Sqrt[2 nu]against WL'sSqrt[2] Sqrt[nu], reached throughStandardDeviation[ChiSquareDistribution[nu]],Nakagami's numericMean,Hoyt's numericMean(4*Sqrt[1/(5*Pi)]*EllipticE[3/4]against(4*EllipticE[3/4])/Sqrt[5*Pi]),SkewNormal'sMean[0,1,1](Sqrt[Pi^(-1)]against1/Sqrt[Pi]) andVarianceGamma's integer-λ branches.Plusterm order in a two-term variance — most distribution variances are a two-term difference and hit it. Named instances:LogLogistic,LogSeries,Coxian,Hyperexponential,TukeyLambda,Wakeby,Weibull(3-arg),Suzuki,PERT,Benini,Dirichlet.Timesfactor order —Pisorts ahead of a function-containing sum in Woxi (4 Pi (…)against WL's4 (…) Pi), andp^(-1+k)*(1-p)against WL's(1-p)*p^(-1+k).
Distributions compute with out-of-range parameters¶
wolframscript -code 'PDF[NormalDistribution[0, -1], 0.3]'
# NormalDistribution::posprm: Parameter -1 at position 2 in
# NormalDistribution[0, -1] is expected to be positive.
woxi eval 'PDF[NormalDistribution[0, -1], 0.3]'
# -0.3813878…
A negative probability density. Constructing the distribution is fine in both
(BetaDistribution[4, -1] echoes silently); the divergence is in using one.
The same applies to PDF, Quantile and Mean. Woxi has a validator, but it
is wired only to DistributionParameterQ and returns a bare boolean — the
message needs the offending parameter's position and value.
Raw moments are missing for most distributions¶
Moment[dist, n] for n ≥ 3 returns unevaluated except for Bernoulli,
Exponential, Uniform, Normal (symbolic n ≤ 4), Gamma, Poisson and ChiSquare,
which cascades into Skewness/Kurtosis not reducing. Two structural notes:
the order-2 raw moment is computed by a separate variance-plus-mean² path, so
Moment[dist, 2] prints expanded (2k+k^2) not WL's factored k(2+k); and
raw Moment[dist, n] diverges anyway for the distributions whose WL answer is
an incomplete-Gamma form.
Beta raw moments are deliberately not added: its central moments are
rational functions in a,b that Woxi's simplifier cannot collapse, so
Skewness/Kurtosis[Beta] would explode into 100+ term expressions against
WL's compact form.
Skewness of a scale-parameter distribution is a positivity rabbit hole —
Skewness[ExponentialDistribution[l]] is 2/(l^(-2))^(3/2)*l^3, value correct
but not simplified to 2 without knowing l > 0.
Central moments of a distribution factor differently¶
wolframscript -code 'ToString[CentralMoment[BernoulliDistribution[p], 3], InputForm]'
# (1 - p)*p*((1 - p)^2 - p^2)
woxi eval 'CentralMoment[BernoulliDistribution[p], 3]'
# p - 3*p^2 + 2*p^3
Even constructing the factored form does not help: Woxi reorders the inner
Plus to (1-p)*p*(-p^2 + (1-p)^2). Skewness and Kurtosis of such
distributions diverge for the same reason.
Probability of a normal tail uses a different Erf form¶
wolframscript -code 'ToString[Probability[x > 1, x \[Distributed] NormalDistribution[0,1]], InputForm]'
# (1 - Erf[1/Sqrt[2]])/2
woxi eval 'Probability[x > 1, x \[Distributed] NormalDistribution[0,1]]'
# (2 - Erfc[-(1/Sqrt[2])])/2
CDF and SurvivalFunction of the same distribution do match. WL is
entangled with itself here — its SurvivalFunction[Normal, 1] is the
unsimplified 1 - Erfc[-(1/Sqrt[2])]/2 while Probability[x > 1] is the Erf
form, so there is no single canonical target for numeric arguments.
Per-distribution scope gaps¶
Left unevaluated because WL's own output is an unreproducible form:
| distribution | what is missing |
|---|---|
Coxian |
mixed repeated/distinct rates, float and symbolic-rate PDF/CDF |
Hypoexponential |
repeated rates (Erlang-style terms) |
TsallisQGaussian |
CDF for numeric q ≠ 1 (WL collapses 2F1 per q), float params |
TukeyLambda |
any λ outside {0, ±1, 1/2, 2}; λ=3 is a Root form in WL |
Wakeby |
symbolic Mean/Variance and symbolic-q Quantile ordering |
VarianceGamma |
CDF (WL's forms are partially unexpanded) |
Hoyt |
CDF (needs MarcumQ) |
SkewNormal |
Skewness/Kurtosis; CDF at a=1 has a special squared form in WL |
NegativeMultinomial |
CDF with symbolic parameters; symbolic StandardDeviation |
HotellingTSquare |
1-ULP coefficient gap for non-integer float parameters |
Wishart |
PDF (needs MatrixGammaFunction), RandomVariate |
FailureDistribution |
duplicated events, Mean (inclusion-exclusion sums) |
DiscreteMarkovProcess |
bare StationaryDistribution, MarkovProcessProperties, CDF, graph constructor |
FirstPassageTime |
symbolic-t PDF, probability-vector initial states |
PERT |
Median (WL gives InverseBetaRegularized/Root forms) |
LogSeries |
RandomVariate, Median, Quantile |
PolyaAeppli |
PDF, CDF |
Benford, WaringYule, 4-arg Gamma, Davis |
unimplemented |
Also: DiscreteUniformDistribution's StandardDeviation is Sqrt[X/12] against
WL's Sqrt[X]/(2 Sqrt[3]), and ExpGammaDistribution's PDF is unimplemented
(exponent Plus order).
MovingMap[Mean, …] stays exact¶
wolframscript -code 'MovingMap[Mean, {1,2,3,4,5}, 2]' # {2., 3., 4.}
woxi eval 'MovingMap[Mean, {1,2,3,4,5}, 2]' # {2, 3, 4}
The trigger is the applied function, not the window: MovingMap[Total, …]
stays exact in both. Mean[{1,2,3}] is exact on its own, yet WL floats it
inside MovingMap. Which functions WL numericizes is not determinable without
a large probe; Woxi's exact behaviour is arguably the better one.
Not reproducible.
Robust statistics that iterate¶
BiweightLocationis iterative in machine floats in WL and returns unconverged trailing digits ({1,2}→1.4999988566906757) even for exact input.BiweightMidvarianceis closed-form and matches.SpatialMedianuses an unconverged Weiszfeld iteration (0.5773502623362886against the true1/Sqrt[3]). Only 1-D inputs, which reduce toMedian, are reproducible.
Not reproducible.
EstimatedDistribution is unimplemented¶
Mean, Quantile and RootMeanSquare of an EventSeries or TemporalData¶
Unevaluated.
Linear algebra¶
Float matrices with complex eigenvalues¶
Wolfram complexifies the whole result and orders each conjugate pair with
+I first: {0. + 1.*I, 0. - 1.*I, 1. + 0.*I}. Woxi gives the value-correct
but form-divergent {0. - 1.*I, 0. + 1.*I, 1.}, and non-block complex cases
stay unevaluated. Complex Eigenvectors for n ≥ 3 are unevaluated too, and
radical eigenvector components order differently ((-Sqrt[5] + I)/3 against
(I - Sqrt[5])/3).
Generic dense float matrices also differ in the last 1–2 digits from WL/LAPACK.
SchurDecomposition and SmithDecomposition factors are non-unique¶
Wolfram's LAPACK eigenvalue ordering along t and the column signs of q are
not reproducible independently ({{1,2},{3,4}} — WL puts −0.372 first, Woxi
5.372). Test the defining properties instead. RealBlockDiagonalForm -> False
(the complex Schur form) is unimplemented.
SmithDecomposition's u and v differ the same way, and WL sometimes keeps
a negative diagonal entry in s ({{1,0},{0,-2}} stays) while Woxi always
normalises to the canonical non-negative SNF — six probe rounds produced no
rule. Woxi's answer is the mathematically canonical one. Not reproducible.
JordanReduce scope¶
Left unevaluated: eigenvalues containing Root objects (WL's diagonal ordering
there is neither Eigenvalues order nor ascending), and inexact matrices with
n ≥ 3 and repeated eigenvalues (WL clusters with a tolerance).
LDLDecomposition and FrobeniusReduce on inexact or symbolic input¶
Left unevaluated. WL's float LDL shows LAPACK-internal rounding
(D = {4., 2.0000000000000004} where plain f64 gives exactly 2.0), and its
FrobeniusReduce returns precision-tagged arbitrary-precision reals whose
precision comes from internal error tracking. Symbolic matrices come back with
unsimplified algorithm-internal quotients.
Also found: FrobeniusReduce[m, Modulus -> -5] reproducibly crashes the
wolframscript kernel (exit 139); Woxi returns unevaluated.
BunchKaufmanDecomposition and PopovDecomposition¶
Both deferred. Bunch-Kaufman's BlockDiagonalMatrix payload has encoder quirks
(a whole-matrix antidiagonal 2×2 pivot is stored as two permuted 1×1 blocks)
plus LAPACK pivot-order replication in exact arithmetic. Popov normalizes over
ℤ[x] rather than ℚ[x] — constant matrices get integer Hermite normal form,
pivots are not made monic, and rational entries get content cleared.
BlockUpperTriangularMatrix / BlockLowerTriangularMatrix¶
Not constructors: they find a matrix's finest block-triangular permutation and return a structured array. The component ordering is a Dulmage–Mendelsohn decomposition needing a perfect bipartite matching first, and WL's tie-breaking would be high-risk to match.
MatrixFunction and MatrixExp on irrational results¶
Both produce value-correct but unsimplified, form-divergent results for
non-diagonal matrices with irrational entries — (3E-E^3)/2-E+E^3 against WL's
E/2+E^3/2, (1+Sqrt[3])/2 against WL's split 1/2+Sqrt[3]/2. Diagonal and
integer-result cases match. MatrixExp on complex/rotation matrices is
value-correct but Woxi simplifies to Cos/Sin where WL 15 keeps E^±I
combinations.
FourierDCTMatrix for n ≥ 5¶
Values agree to ~15 digits; the radical display diverges — WL canonicalizes
Sqrt[(5/8+Sqrt[5]/8)/5] where Woxi gives Sqrt[5/8+Sqrt[5]/8]/Sqrt[5], and
WL folds 2/(2 Sqrt[3]) to 1/Sqrt[3].
SymmetricReduction two-argument form¶
Value correct always, but the display order diverges for some higher-degree
two-variable cases: x^3+y^3 gives (x+y)^3-3*x*y*(x+y) in Woxi against WL's
-3*x*y*(x+y)+(x+y)^3. The three-argument form is fully conformant.
Symmetrize of a symbolic tensor¶
Symmetrize[T, Symmetric[{1,2,3}]] for a bare symbol T should give the
explicit (T + TensorTranspose[T, {2,1}] + …)/6. Woxi leaves it unevaluated:
TensorTranspose on a symbol errors, and wolframscript's term order truncates
trailing fixed points.
ArrayFlatten[a, r]¶
The two-argument form is unimplemented (Woxi reports argx). Ragged
block-tensor assembly, rarely used.
RotationMatrix / RotationTransform about a symbolic axis¶
Woxi leaves a 3D rotation about a non-numeric axis unevaluated
(RotationTransform[Pi, {0, 0, x}, {1, 0, 0}] echoes). Wolfram does return
something, but only as an unsimplified artifact of its internal Gram–Schmidt:
wolframscript -code 'RotationMatrix[Pi, {0, 0, x}]'
# {{-((x Conjugate[x])/Abs[x]^2), 0, 0}, {0, -1, 0}, {0, 0, (x Conjugate[x])/Abs[x]^2}}
Note the asymmetry — entry (1,1) keeps the un-cancelled x Conjugate[x]/Abs[x]^2
(which is just 1) while entry (2,2) is a plain -1, so the two diagonal
entries of the same rotation print differently. A fully symbolic axis
{a, b, c} produces roughly 8 KB of nested Conjugate[1/Sqrt[a Conjugate[a] +
b Conjugate[b]]] terms, with Sqrt[(a Conjugate[a] + b Conjugate[b])/(a
Conjugate[a])] denominators that are singular exactly on the coordinate axes.
The output is not a canonical form (and is not a real rotation matrix for
complex components), so there is nothing stable to conform to. Not
reproducible; the affected unit test is listed in EXACT_EXPR_SKIP in
tests/wolframscript/verify_unit_tests.ts.
NumberFieldDiscriminant¶
Non-maximal orders are unevaluated (Sqrt[2]+Sqrt[3] → WL 2304 via a full
conductor/Round-2 computation), as are non-monic minimal polynomials of degree
≥ 3.
Expression structure and evaluation¶
Rubi loads and integrates, but not everything it computes agrees¶
The Rubi rule base is the densest available exercise of the
pattern matcher and the definition store: 7000 rules, all read back out of
DownValues and rewritten before use. It loads unmodified and integrates, and
on a 30-integral sample 19 answers are identical to wolframscript's. What is
left, all of it ordinary Woxi behaviour rather than anything about the package:
- Loading takes about a minute against roughly twenty seconds under
wolframscript, and Rubi's step-display machinery ($LoadShowSteps = True, the default) rewrites all 7000 rules on load — which Woxi has not finished after half an hour and ten gigabytes.Steps,StepandStatsare therefore out of reach. Int[Sin[x]^3*Cos[x]^2, x]andInt[Sin[x]*Cos[x]^3, x]run for minutes and exhaust memory.wolframscriptanswers both instantly.Int[ArcSin[x], x]comes back asDefer[Int][ArcSin[x], x]: the rule that should fire never does, and theCannotIntegratefallback wins. So does everything reached throughInt[x/Sqrt[1 - x^2], x], which Woxi answers with aHypergeometric2F1where the elementary rule applies.Int[1/(1 + x^3), x]is missing itsArcTanterm.- Equivalent but differently shaped answers are common and not bugs as
such:
Int[Sec[x]^2, x]isSec[x]*Sin[x]rather thanTan[x],Int[E^x*x, x]is-Gamma[2, -x]rather thanE^x*(x - 1), and sums come out in Woxi's ownPlusorder.
Derivative[n][f][x] is stored flat, so structural functions see three parts¶
wolframscript -code 'Head[Derivative[1][g][x]]' # Derivative[1][g]
woxi eval 'Head[Derivative[1][g][x]]' # Derivative
wolframscript -code 'Length[Derivative[1][g][x]]' # 1
woxi eval 'Length[Derivative[1][g][x]]' # 3
wolframscript -code 'Apply[f, Derivative[1][g][x]]' # f[x]
woxi eval 'Apply[f, Derivative[1][g][x]]' # f[1, g, x]
Woxi stores it flat rather than as nested curried calls. The renderer prints it
correctly and D returns the right thing, so only structural introspection
diverges — Head, Length, Part, Level, Map and Apply all leak the
internal shape.
Unevaluated[…] is not transparent to structural functions¶
wolframscript -code 'Unevaluated[1 + 1] // Head' # Plus
woxi eval 'Unevaluated[1 + 1] // Head' # Unevaluated
wolframscript -code 'Depth[Unevaluated[1 + 1]]' # 2
woxi eval 'Depth[Unevaluated[1 + 1]]' # 3
Length, AtomQ and Head now agree; Depth, First and Part still see
the wrapper (Part[Unevaluated[1+2], 1] gives 3 where WL gives 1). The
arithmetic consumers are also open: Unevaluated[1+1] + 1 is
1 + Unevaluated[1 + 1] rather than 3, and Total/Identity behave the same
way. Woxi's model is per-consumer stripping because the real rule is not the
published "strip unless HoldAllComplete".
The same per-consumer stripping is why a wrapper a pure function's body produced is stripped one level too eagerly. WL strips only wrappers written literally in an argument list, so a produced one survives into any consumer:
wolframscript -code 'Head[(Unevaluated[Sequence[#, #^2]] &)[3]]' # Unevaluated
woxi eval 'Head[(Unevaluated[Sequence[#, #^2]] &)[3]]' # Sequence
Structural positions agree — {0, (Unevaluated[Sequence[#, #^2]] &)[3], 9} is
{0, Unevaluated[Sequence[3, 3^2]], 9} and f[…] keeps the wrapper in both —
and so do the strippers whose wrapper was literal. Only an
argument-consuming built-in (ToString, Length, Head) fed a produced
wrapper diverges; telling the two apart needs the literal-ness of each
argument threaded through to the built-in dispatch.
A non-terminating NestWhile returns a wrong answer instead of not terminating¶
wolframscript -code 'TimeConstrained[NestWhile[#/2 &, 16, UnsameQ, 2], 5, timeout]' # timeout
woxi eval 'NestWhile[#/2 &, 16, UnsameQ, 2]' # 1/2^10000
Successive halvings of 16 are never SameQ, so this iterates forever in
wolframscript. Woxi silently stops after about 10 000 iterations and returns
the value it had reached, which is neither wolframscript's behaviour nor an
error.
Return unwinds through ordinary argument evaluation¶
Wolfram treats Return[x] as an ordinary expression that only three things
consume (a definition body, Do, Scan), with CompoundExpression, While,
For, Module, Block and With passing it upward intact. Everything else
keeps it as a value: Table[Return[1], {2}] is {Return[1], Return[1]},
Catch[Return[1]] is Return[1], {Return[1], 2} and f[Return[1]] and
Return[1] + 2 all stand.
Woxi raises Return as a signal, so each boundary must be explicit. Table,
Map, Select and With are wired; a Return reached by ordinary argument
evaluation still unwinds — {Return[1], 2} gives 1, Catch[Return[1]] gives
1, Function[Return[3]][] gives 3.
x = 2 x does not recurse¶
wolframscript evaluates the right-hand side, assigns the own-value x → 2*x,
and then re-evaluates it on every lookup until $RecursionLimit:
Woxi returns 2*x and treats the own-value as a fixed-point substitution.
Format upvalues are stored as UpValues rather than FormatValues¶
wolframscript -code 'c /: Format[c] := "see"; ToString[UpValues[c], InputForm]' # {}
woxi eval 'ClearAll[c]; c /: Format[c] := "see"; UpValues[c]'
# {HoldPattern[Format[c]] :> see}
Wolfram files a Format definition made through /: under FormatValues,
not UpValues, so UpValues[c] comes back empty. Woxi has no separate
FormatValues table and reports the rule as an upvalue. The rest of the
UpValues / TagSet / TagUnset family agrees with wolframscript.
Format[…] is neither held nor form-aware¶
wolframscript -code 'Head[Format[x + y]]' # Format
woxi eval 'Head[Format[x + y]]' # Plus
wolframscript -code 'Head[Format[x + y, OutputForm]]' # Format
woxi eval 'Head[Format[x + y, OutputForm]]' # Symbol
Wolfram keeps Format[expr] and Format[expr, form] unevaluated — Length
is 1 or 2, part 1 is the expression — and only applies a display rule, the
same model Definition follows.
The second argument is also ignored, so every form but OutputForm renders
the plain expression:
wolframscript -code 'Format[Sqrt[x], TeXForm]' # \sqrt{x}
woxi eval 'Format[Sqrt[x], TeXForm]' # Sqrt[x]
wolframscript -code 'Format[x/y, TeXForm]' # \frac{x}{y}
woxi eval 'Format[x/y, TeXForm]' # x/y
wolframscript -code 'Format[x + y, StandardForm]' # RowBox[{x, +, y}]
woxi eval 'Format[x + y, StandardForm]' # x + y
wolframscript -code 'Format["ab", InputForm]' # "ab"
woxi eval 'Format["ab", InputForm]' # ab
StandardForm and TraditionalForm want boxes, which ToBoxes already
produces correctly, and TeXForm / InputForm want the corresponding
renderer. An unsupported form (Format[x^2, FullForm]) stays unevaluated in
WL and prints as Format[x^2, FullForm].
DownValues drops a /; guard on a compound left-hand side¶
f[x_] := x^2 /; x > 0 round-trips, but
k[(a_. + b_.*x_)^m_., x_Symbol] := … /; FreeQ[…] reports
{HoldPattern[k[…]] :> a + b + m} with no /;. The guard is enforced at
call time — it lives in a per-parameter condition slot the reconstruction
never re-attaches. This is the single remaining blocker for running Rubi's
FixIntRules[], which reads DownValues[Int], sees unguarded rules and writes
them back, destroying the rule base.
A related display-only residue: a trailing-sequence list rule's DownValues
shows {a_, __} rather than {a_, b__}, because the trailing element's name
is not recoverable.
ToExpression of a definition¶
ToExpression["h[x__] := {x}; h[1, 2]"] gives h[1, 2] while
ToExpression["zz = 7; zz + 1"] gives 8 — definitions made through
ToExpression do not take effect for the rest of the same string.
Context of an unknown context¶
Context["zzzabc"]returns"zzz" in Woxi; wolframscript reports
Context::notfound and stays unevaluated.
Module rename artefacts and InformationData¶
Module locals are named loc$1 here and loc$ in wolframscript (and the
counter value is a session number that can never be pinned), and
InformationData prints its keys unquoted. wolframscript also carries its own
WolframScript`` context, which Woxi has no equivalent for — so a bare$ContextPath` can never match.
Legacy package names¶
Any Combinatorica…orPolyhedronOperations… symbol Woxi implements (e.g.
UnrankPermutation, Truncate, Stellate) evaluates where wolframscript
leaves it unevaluated, because the package is not loaded. Woxi has no package
system for Needs to load into, so the qualified names are always live. With
the package loaded the results agree.
$VersionNumber is a string¶
Woxi returns its version string (v0.3.0-46-g…) rather than a number, so
ToString[$VersionNumber] and any $VersionNumber >= 9 test break.
Manipulate's Initialization is not scoped¶
Manipulate[…, Initialization :> (f[x_] := …)] leaves f defined in the
global scope, so a later cell sees it. wolframscript keeps it inside the
DynamicModule the Manipulate wraps itself in and f[3] echoes back. Woxi has
no such module, and its controls re-resolve the body on every frame, so the
definitions have to outlive the call.
Get returns raw text¶
Get returns an Expr::Raw, so List @@ Get["PacletInfo.m"] fails, and
SystemPrivate$InputFileName is never set, so a package cannot locate its
own directory. DumpSave is unimplemented, so a paclet's MX fast-load path
can never work.
Lists, associations and structured objects¶
ListCorrelate / ListConvolve have no multi-dimensional overhang¶
wolframscript -code 'ToString[ListCorrelate[{{1, 1}, {1, 1}}, {{a, b, c}, {d, e, f}, {g, h, i}}, 1], InputForm]'
# {{a + b + d + e, b + c + e + f, a + c + d + f},
# {d + e + g + h, e + f + h + i, d + f + g + i},
# {a + b + g + h, b + c + h + i, a + c + g + i}}
woxi eval 'ListCorrelate[{{1, 1}, {1, 1}}, {{a, b, c}, {d, e, f}, {g, h, i}}, 1]'
# ListCorrelate[{{1, 1}, {1, 1}}, {{a, b, c}, {d, e, f}, {g, h, i}}, 1]
The two-argument multi-dimensional form is correct; only the overhang path
(k / {kL, kR}, padding, generalized g/h) is one-dimensional, and it
stays unevaluated for a rank-2 kernel rather than answering.
The 7th argument, a level specification, is unimplemented for every rank:
wolframscript -code 'ToString[ListCorrelate[{x, y}, {a, b, c}, 1, p, Times, Plus, 1], InputForm]'
# {a*x + b*y, b*x + c*y, c*x + p*y}
woxi eval 'ListCorrelate[{x, y}, {a, b, c}, 1, p, Times, Plus, 1]'
# ListCorrelate::argb: called with 7 arguments; between 2 and 6 arguments are expected.
Total groups negative levels globally rather than per parent¶
wolframscript -code 'Total[{{1, 2}, {3, {4, 5}}}, {-1}]' # {3, {3, 9}}
woxi eval 'Total[{{1, 2}, {3, {4, 5}}}, {-1}]' # {{1, 2}, {3, 9}}
Woxi reads {-1} as the deepest level of the whole expression — level 3 here,
so it answers Total[…, {3}]. wolframscript sums the depth-1 parts grouped by
their immediate parent, which leaves the depth-1 atom 3 alone and reduces
only {4, 5}.
Every other head measures a negative level as the part's own depth; Total
is the exception because its traversal also carries AllowedHeads and the
head-preservation rules.
AssociationThread does not take a scalar key¶
wolframscript -code 'AssociationThread[3, {1, 2}]' # <|3 -> 2|>
wolframscript -code 'AssociationThread["ab", {1, 2}]' # <|ab -> 2|>
woxi eval 'AssociationThread[3, {1, 2}]' # AssociationThread[3, {1, 2}]
A scalar value is shared across the keys, but a scalar key against a list of values keeps only the last value, which is peculiar enough that generalising from two examples would be guessing. Left unevaluated on purpose; it echoes rather than aborting.
Mixed association/list set operations¶
Union, Intersection and Complement mixing an association with a list stay
unevaluated in both engines, but WL emits ::heads where Woxi emits
::normal.
Take with a zero start index¶
Take[{1,2,3}, {0, -1}] and {0, 3} are {} in wolframscript and an error in
Woxi. Every other {0, n} errors in WL too, so the rule there is obscure.
SparseArray slices and Select¶
sa[[1]]andsa[[All, 2]]return a dense list (or, for[[All, 2]], garbage) where WL returns a sparse sub-array.Select[sa, pred]returns a brokenSparseArray[]. WL returns a dense list when the background value fails the predicate and stays sparse when it passes — a predicate-dependent choice.SparseArray op listandSparseArray + SparseArrayreturn a dense list in Woxi where WL keeps the result sparse. The values match throughNormal.Map[f, sa]at rank ≥ 2 densifies; WL keeps the sub-rows sparse.AdjacencyMatrix[g]returns a dense matrix rather than aSparseArray, soAdjacencyMatrix[g] // MatrixFormprints differently.
Threaded[scalar] gives garbage¶
wolframscript -code 'Threaded[1] + {1,2,3}'
# Threaded::rarray … and the operation unevaluated as {1, 2, 3} + Threaded[1]
woxi eval 'Threaded[1] + {1,2,3}'
# {Threaded[2], Threaded[3], Threaded[4]}
The Threaded argument must be rank ≥ 1; a scalar inner is invalid. A
two-argument special case was tried and reverted because it turned the
three-argument case from garbage into a crash.
ReplaceAll head replacement in two exotic corners¶
- Association under a symbol blank: Woxi descends into keys and values keeping
the association form (
<|f[k] -> f[v]|>); WL givesf[Association][k -> f[v]], leaving keys and the innerRulehead alone. - Held infix:
Hold[2+a] /. x_Symbol :> f[x]leaves the inner heldPlushead unrewritten (f[Hold][2 + f[a]]against WL'sf[Hold][f[Plus][2, f[a]]]).
A failed nested Part rebuilds the whole chain¶
wolframscript leaves Part[m, 3] unevaluated and applies the next index to
that expression, so m[[3]][[1]] returns m and m[[1]][[1]][[1]] reports
Part::partd: Part specification 1[[1]]. Woxi rebuilds the chain against the
base and prints {{1, 2}, {3, 4}}[[1,1,1]]. Both agree on every in-range read;
this shows only on the error path.
DateInterval canonical form¶
Woxi canonicalizes to a 4-argument form with "Day" granularity; current
wolframscript produces a 6-argument form and derives granularity from the
input (Year for a pair of year DateObjects). Both the arity and the
granularity diverge. The 2- and 3-argument constructor calls fall through
unevaluated.
Duration of a Video, DateInterval or Play¶
Valid objects with no message, but unmeasured — silently unevaluated. An
interval-form SoundNote has absolute-placement semantics that are also not
modelled.
InterpolatingFunction's raw InputForm¶
WL prints its internal representation
(InterpolatingFunction[{{1, 3}}, {5, 3, 0, {3}, {3}, 0, …}, …]) while Woxi
prints InterpolatingFunction[domain, data, order]. The property queries
("Domain", "Grid", "ValuesOnGrid", …) all match.
Compile produces a different object¶
Woxi's two-part CompiledFunction[specs, body] is nothing like WL's
eight-part bytecode object, so the printed form can never match. The binding
rules (untyped defaults to _Real, element-by-element conversion of rank-n
real arguments, result type following the signature) do conform.
TimeSeries and TemporalData¶
The installed wolframscript (Tabular-backed internals) disagrees with Woxi's
verified tests on several points — 0-based "Times", Length counting points,
Part giving a {time, value} pair, "Values" as a TabularColumn. These
are version drift, not Woxi bugs. Likewise Mean/Variance/Median/
StandardDeviation of a whole-valued series return machine reals in
wolframscript (its packed Integer64 column being promoted) and stay exact in
Woxi.
Still genuinely open: WL keeps a TemporalData[Automatic, {…}, False, version]
internal form that Woxi normalizes to TimeSeries, and MovingMap[f, x, 0]
should report MovingMap::wsizen (WL then returns a mangled DisplayForm
value that is not worth reproducing, so Woxi's width check is silent).
Dataset variance¶
Variance[Dataset[{1., 2., 3.}]] is 1 against WL's 1.0000000000000002 —
the float-noise class above.
Patterns, strings and parsing¶
x_ y_ is mis-parsed¶
wolframscript -code 'Hold[x_ y_] // FullForm'
# Hold[Times[Pattern[x, Blank[]], Pattern[y, Blank[]]]]
woxi eval 'Hold[x_ y_] // FullForm'
# Hold[Pattern[x, BlankSequence[y_]]]
The parser merges the two _ across the space into a BlankSequence whose
head becomes y_. x_ + y_ parses correctly; only the implicit-times
juxtaposition is broken, so any implicit-times product of patterns is affected
(ReplaceList[a b c, x_ y_ :> {x,y}] gives {} where WL gives six splits).
Writing Times[x_, y_] explicitly works.
A bare Span cannot follow a Rule¶
a -> 2 ;; 3 fails to parse ("expected SpanSep"); WL reads it as
Rule[a, Span[2,3]] because ;; binds tighter than ->. Parenthesizing
works. This blocks the documented StringExtract[s, sep -> i ;; j] form.
X::y on the right of ->/:> fails the same way.
f[{1, 2} // Total -> 3] does not parse¶
WL reads it as {1,2} // (Total -> 3), i.e. the postfix function is the rule.
Making the rule's left side a postfix application parses it with the wrong
precedence, which is worse than the parse error.
(a|b)|c loses its grouping¶
Explicit parentheses should give Length 2 (nested) in WL; Woxi's parser
produces the same flat 3-argument form as a|b|c.
Parse-time messages do not exist¶
{a,,b} and f[a,] parse to Null as they should, but wolframscript also
prints Syntax::com: Warning: comma encountered with no adjacent expression.
once per omitted comma, at read time. Woxi has no parse-time message channel at
all — wiring one through the AST conversion would duplicate the warning,
because a cell is parsed more than once per evaluation. The same absence means
a \!\(…\) escape with a bad form tag is left literal rather than reported as
MakeExpression::boxfmt.
Empty matches are dropped¶
wolframscript -code 'StringCases["abcd", ___]' # {abcd, }
woxi eval 'StringCases["abcd", ___]' # {abcd}
wolframscript -code 'StringCases["abcd", ___, Overlaps -> True]' # {abcd, bcd, cd, d, }
StringPosition["abcd", ___] likewise drops WL's trailing {5, 4}. The
default and Overlaps -> True paths exclude empty regex matches and never
visit the position one past the last character; the Overlaps -> All path
handles them correctly.
Overlaps -> All with zero-width assertions¶
Any zero-width assertion other than a leading StartOfString or trailing
EndOfString is evaluated against the probed slice rather than the whole
string, so WordBoundary ~~ __ under Overlaps -> All diverges.
String pattern back-references need backtracking¶
A repeated pattern name is a back-reference (x_ ~~ x_ matches two equal
characters). The regex engine has none, so Woxi emits a duplicate capture and
compares the two after the match — which cannot backtrack to find an
alternate assignment. Fine for fixed-width single-character blanks;
variable-width (x__ ~~ x__) greedy assignment can miss a valid split.
Look-around and back-references in RegularExpression¶
RegularExpression["(?=[A-Z])"] cannot compile (Woxi now reports
RegularExpression::badregex and leaves the call unevaluated rather than
aborting). WL supports look-around and \1.
DatePattern element coverage¶
AMPM, Quarter, YearShort and other element names are unimplemented, as is
DateDelimiters-style probing beyond the four verified separators [/-.:].
Longest/Shortest in a definition¶
The rule path (/., Cases, MatchQ) honours them; the definition path
strips them, so f[Longest[x__], y__] := … splits shortest-first.
Unimplemented argument forms found by diffing against SyntaxInformation¶
Woxi's arity table can be diffed mechanically against wolframscript's own declared signatures:
(* for each implemented function name *)
"ArgumentsPattern" /. SyntaxInformation[Symbol["System`" <> name]]
Counting the required and optional slots (skipping anything with
OptionsPattern or a BlankSequence, which are unbounded) and comparing
against Woxi's declared maximum turns up every documented argument form that
is missing. The sweep originally found 80; the 67 below are what is left.
Each one is a form wolframscript accepts and Woxi rejects on arity — so the
failure mode is an ::argt / ::argb / ::argx message rather than a wrong
answer, which makes them safe but individually invisible.
| Function | Woxi | WL up to | ArgumentsPattern |
|---|---|---|---|
AbsoluteCorrelation |
1–2 | 3 | {_, _., _.} |
AiryAiZero |
1–1 | 2 | {_, _.} |
AiryBiZero |
1–1 | 2 | {_, _.} |
AngerJ |
2–2 | 3 | {_, _, _.} |
AngleBisector |
1–1 | 2 | {{_, _, _}, _.} |
ArcCurvature |
2–2 | 3 | {{__}, _, _.} |
BesselYZero |
2–2 | 3 | {_, _, _.} |
BooleanMaxterms |
2–2 | 3 | {_, _., _.} |
BooleanMinterms |
2–2 | 3 | {_, _., _.} |
CellularAutomaton |
1–3 | 4 | {_, _., _., _.} |
CenterArray |
1–3 | 4 | {_, _., _., _.} |
CharacterName |
1–1 | 2 | {_, _.} |
CircleThrough |
1–1 | 3 | {{__}, _., _.} |
ConstantArray |
2–2 | 3 | {_, _, _.} |
CoordinateBoundingBoxArray |
1–3 | 4 | {{_, _}, _., _., _.} |
CoordinateBoundsArray |
1–3 | 4 | {{__}, _., _., _.} |
Correlation |
1–2 | 3 | {_, _., _.} |
Counts |
1–1 | 2 | {_, _.} |
Covariance |
1–2 | 3 | {_, _., _.} |
DigitCount |
1–3 | 4 | {_, _., _., _.} |
DigitSum |
1–2 | 3 | {_, _., _.} |
EulerAngles |
1–1 | 2 | {_, Optional[{_, _, _}]} |
FindLinearRecurrence |
1–1 | 2 | {_, _.} |
FrenetSerretSystem |
2–2 | 3 | {{__}, _, _.} |
GammaDistribution |
2–2 | 4 | {_, _, _., _.} |
GompertzMakehamDistribution |
2–2 | 4 | {_, _, _., _.} |
GroupOrbits |
2–2 | 3 | {_, Optional[{__}], _.} |
GroupStabilizer |
2–2 | 3 | {_, {__}, _.} |
Groupings |
2–2 | 3 | {_, _, _.} |
HarmonicNumber |
1–2 | 3 | {_, _., _.} |
Head |
1–1 | 2 | {_, _.} |
ImageAdjust |
1–2 | 4 | {_, _., Optional[{_, _}], Optional[{_, _}]} |
Inner |
3–4 | 5 | {_, _, _, _., _.} |
InverseChiSquareDistribution |
1–1 | 2 | {_, _.} |
InverseErf |
1–1 | 2 | {_, _.} |
InverseGammaDistribution |
2–2 | 4 | {_, _, _., _.} |
InverseGaussianDistribution |
2–2 | 3 | {_, _, _.} |
KendallTau |
2–2 | 3 | {_, _., _.} |
Latitude |
1–1 | 2 | {_., _.} |
LatitudeLongitude |
1–1 | 2 | {_., _.} |
LegendreP |
2–3 | 4 | {_, _, _., _.} |
LegendreQ |
2–3 | 4 | {_, _, _., _.} |
ListConvolve |
2–6 | 7 | {_, _, _., _., _., _., _.} |
ListCorrelate |
2–6 | 7 | {_, _, _., _., _., _., _.} |
Longitude |
1–1 | 2 | {_., _.} |
MaximalBy |
1–3 | 4 | {_, _., _., _.} |
MeijerG |
3–3 | 4 | {{{___}, {___}}, {{___}, {___}}, _, _.} |
MinimalBy |
1–3 | 4 | {_, _., _., _.} |
MultipleHarmonicNumber |
1–2 | 3 | {_, Optional[{__}], Optional[{__, _}]} |
NotebookDirectory |
0–0 | 1 | {_.} |
ParentDirectory |
0–1 | 2 | {_., _.} |
PerfectNumber |
1–1 | 2 | {_, _.} |
PolyLog |
2–2 | 3 | {_, _, _.} |
PositionLargest |
1–2 | 3 | {_, _., _.} |
PositionSmallest |
1–2 | 3 | {_, _., _.} |
Precedence |
1–1 | 2 | {_, _.} |
QuantityQ |
1–1 | 2 | {_, _.} |
RiceDistribution |
2–2 | 3 | {_, _, _.} |
SpearmanRho |
2–2 | 3 | {_, _., _.} |
Subsequences |
1–2 | 3 | {_, _., _.} |
SubsetReplace |
1–2 | 3 | {_, _., _.} |
Symmetrize |
1–1 | 2 | {_, _.} |
SyntaxQ |
1–1 | 2 | {_, _.} |
ToBoxes |
1–1 | 2 | {_, _.} |
Uncompress |
1–1 | 2 | {_, _.} |
Unique |
0–1 | 2 | {_., Optional[{__}]} |
WeberE |
2–2 | 3 | {_, _, _.} |
Notes on the ones already looked at:
MinimalBy/MaximalBy/DigitCount: wolframscript appears to ignore the extra trailing argument (MinimalBy[{{1,2},{3,1},{2,5}}, Last, 2, x]gives the same answer as withoutx), so there may be nothing to match.Counts[list, n]: wolframscript rejects a non-list second argument withCounts::invl, so the slot is not a useful form either.PositionLargest/PositionSmallest: the remaining slot is an orderfun, but wolframscript rejectsGreaterandLessthere with::nord3(it wants anOrder-style comparison returning ±1/0), andOrderitself is the default — so there is little behaviour to match.- The generalized distribution constructors (
GammaDistribution2→4,InverseGammaDistribution2→4,GompertzMakehamDistribution2→4,RiceDistribution2→3,InverseGaussianDistribution2→3,InverseChiSquareDistribution1→2) have real closed forms, but their output isPiecewisewrappingGammaRegularized,LaguerreLand nestedGammaquotients. Matching wolframscript there means matching its exact symbolic canonicalization.
wolframscript -code 'ToString[PDF[GammaDistribution[a, b, g, m], x], InputForm]'
# Piecewise[{{(g*((-m + x)/b)^(-1 + a*g))/(b*E^((-m + x)/b)^g*Gamma[a]), x > m}}, 0]
woxi eval 'PDF[GammaDistribution[a, b, g, m], x]'
# GammaDistribution::argrx: GammaDistribution called with 4 arguments; 2 arguments are expected.
A second sweep, comparing declared arities against the arities the dispatch
actually handles, found these still open: Transliterate[s, scheme],
SumConvergence[f, n, Assumptions -> …], LexicographicSort[list, ord],
SavitzkyGolayMatrix[r, order, deriv], and the 3-argument forms of
ClusteringComponents, ComponentMeasurements, ImageValue,
WarpingCorrespondence, plus EventSeriesLookup ¾-argument.
Declared but dead options¶
For each option a head declares in Options[f], setting it should change the
answer. These do not:
NIntegrate'sMaxRecursion,MinRecursion,WorkingPrecision,MaxPoints,PrecisionGoal,AccuracyGoal.NSumandNProductacceptWorkingPrecisionand ignore it.Root[…, ExactRootIsolation -> True]stays unevaluated.D[f[x] g[x], x, NonConstants -> {f}]differentiatesfanyway; WL holds it asD[f[x], x, NonConstants -> {f}]. (The plainD[x y, x, NonConstants -> {y}]case works.)SetOptions[Plot, PlotStyle -> …]maintains the option list but does not reach the renderer.Options[ListPlot]is{}against WL's 70 entries;Plot3D85,Histogram61,BarChart64,Graphics39. OnlyPlothas a table.OptionValue[Plot, Axes]givesAxesinstead ofTrue, and$DisplayFunction/$PerformanceGoalare undefined soOptionValue[Plot, DisplayFunction]returns the unresolved symbol.OptionValue's::repmessage for a non-rule element of the explicit option list is not emitted, and WL's own rule for when it fires is unclear (OptionValue[Plot, Axes, Hold]reports it,OptionValue[Plot, 5, Frame]does not).
Messages and error handling¶
Expressions inside message text print in InputForm¶
WL formats a message's substituted expressions the way the front end shows
them (Times as a space), Woxi formats them like InputForm (*):
wolframscript -code '2 x = 5' # Set::write: Tag Times in 2 x is Protected.
woxi eval '2 x = 5' # Set::write: Tag Times in 2*x is Protected.
The message tag, its arguments and the returned value all agree; only the
rendering of an embedded expression differs, and it does so for every
message that embeds one (Set::write, ImageAdjust::arg2, …). Each
message site formats with expr_to_string; matching WL means routing them
through the OutputForm renderer instead.
Outer does not report mismatched heads¶
wolframscript -code 'Outer[f, h[1, 2], {a, b}]'
# Outer::heads: Heads List and h at positions 3 and 2 are expected to be the same.
# Outer[f, h[1, 2], {a, b}]
woxi eval 'Outer[f, h[1, 2], {a, b}]'
# Outer[f, h[1, 2], {a, b}]
The result is right — the call stays unevaluated either way — but the
Outer::heads message is missing. Every other Outer form checked
(per-list levels, general heads, operator form) agrees.
Rasterize with an unknown element aborts the whole script¶
wolframscript -code 'Print[ToString[Rasterize[x, "Text"], InputForm]]; Print["after"]'
# Rasterize::elmntavl: "Text" is not an available element. Possible elements
# include "BoundingBox", "Data", "Graphics", "RasterSize", "Regions", and "Image".
# $Failed
# after
woxi eval 'Print[Rasterize[x, "Text"]]; Print["after"]'
# Error: Evaluation error: Rasterize: unsupported expression type
The hard error kills the run, so nothing after it evaluates. Rasterize
should emit Rasterize::elmntavl and return $Failed.
Twenty-two heads raise a hard error for a bad first argument¶
wolframscript -code 'ImageCrop[{1, 2, 3}, -1]'
# ImageCrop::imgvinv: Expecting an image, graphics or video instead of {1, 2, 3}.
# ImageCrop[{1, 2, 3}, -1]
woxi eval 'ImageCrop[{1, 2, 3}, -1]'
# Error: Evaluation error: ImageCrop: first argument is not an Image
A sweep of the 855 heads whose arity admits two arguments, against nine
degenerate argument pairs, found 227 calls that abort the enclosing evaluation
instead of reporting. What remains is this one shape, spread over While,
TuringMachine, Find, ReadList, Piecewise, ImageApply,
ImageAssemble, ImageCollage, ImageCompose, ImageCrop, NMinimize,
NMaximize, FindSequenceFunction, RealDigits, Dt, FrenetSerretSystem,
ArcCurvature, MinimalPolynomial, Root, NumberFieldSignature,
WordCounts and DivisorSigma.
The argument is nonsense in every case, so no correct result is lost — but a hard error takes the whole script down where a message does not.
An operator form that fails names the flattened call, not what was written¶
wolframscript -code 'Select[EvenQ][5]'
# Select::normal: Nonatomic expression expected at position 1 in Select[EvenQ][5].
# Select[EvenQ][5]
woxi eval 'Select[EvenQ][5]'
# Select::normal: Nonatomic expression expected at position 1 in Select[5, EvenQ].
# Select[5, EvenQ]
f[spec][data] is rewritten to f[data, spec] before dispatch, and when the
call then fails both the message and the echo describe the rewrite. The same
happens for SortBy, KeyTake and Nearest, so this is one issue in the
operator-form dispatch rather than a per-function one.
Iteration constructs substitute their variable instead of binding it¶
Table, Sum and Product rewrite the iterator symbol into the body rather
than binding it dynamically the way Wolfram does, so a held occurrence is
replaced instead of left standing: Table[Hold[k], {k, 1, 2}] gives
{Hold[1], Hold[2]} where Wolfram gives {Hold[k], Hold[k]}, and
Sum[Hold[k], {k, 1, 2}] gives Hold[1] + Hold[2] instead of 2 Hold[k].
The substitution is capture-avoiding, so a nested Module/With/Function
binder of the same name is left alone; only unbound held positions diverge.
$GeoLocation and location-defaulting messages¶
Woxi returns Missing["NotAvailable"] — matching an offline wolframscript —
but does not emit $GeoLocation::dloff or the per-function Fn::geoloc.
Message repetition and 2D layouts¶
DensityPlot's degenerate-range message is reported under its internal context name (Visualization`Core`DensityPlot::plld) and emitted twice; Woxi cannot produce that symbol name and leavesDensityPlotout of the check entirely.PolarPlot,ContourPlot3Dand two-parameterParametricPlotemit the message twice (a second internal pass); Woxi emits it once.Hash::invhashis printed twice by wolframscript for an unknown algorithm.Quantity::compatis emitted twice by wolframscript, without quotes, and its two unit names appear in an order that is not input order (Kilograms + Meters→ "Meters and Kilograms"). Woxi quotes them and emits once.- Messages that embed a fraction are rendered as 2D layouts by wolframscript
and 1D by Woxi. This is systemic across the distribution and
Select::normalmessage families. - Message multiplicity in general is not comparable: wolframscript
re-evaluates a failing specification, so it prints some messages twice, and
applies
General::stopafter three identical ones.
ArgMax with an invalid constraint form¶
ArgMax[{list}, fn] — WL emits ArgMax::consf, Woxi silently echoes.
Failure prints its formatted message¶
In script mode wolframscript does not print a Failure[…] expression — it
prints the failure's formatted message, wrapping every substituted parameter
in DisplayForm[TagBox[…, Short[#1, 3] &]]. That is typeset internals leaking
into script mode; Woxi prints the expression and renders ["Message"] cleanly.
Deliberately not reproduced — test a Failure's keys, not its printed form.
Graphics and plotting¶
Woxi renders its own SVG; wolframscript prints -Graphics- and exports
cairo-rendered SVG, so pixel or markup equality is never the goal. What follows
is where the drawn result differs.
The default plot style is not Wolfram's¶
Every Woxi plot draws its first curve in #5E81B5 at 1.5 px. Wolfram draws it
in RGBColor[0.24, 0.6, 0.8] = (61, 153, 204) at AbsoluteThickness[2]. Woxi
is using the ColorData[97] palette where Plot has its own
DefaultPlotStyle, readable from the kernel:
Woxi already writes the right values in its .nb GraphicsBox export; only
the SVG renderer disagrees. Changing it moves 153 snapshots and about 40 inline
colour assertions at once.
Graphics with Axes clips its edge tick labels¶
Graphics[…, Axes -> True, ImageSize -> {400, 400}] lays its plotting area out
from x = 0 with only a right-hand margin, so the tick label at the range
minimum is centred on x = 0 and half of it falls outside the canvas; the
topmost y label is clipped the same way. Wolfram insets the area far enough for
both. This is the Graphics renderer, not the one Plot uses.
Tick step from the padded range¶
Woxi derives the tick step from the padded display range, so a ListPlot
whose data ends at 6e15 can pick a coarser step than Wolfram, which uses the
data range.
ParametricPlot samples uniformly¶
max(PlotPoints, 500) points, no adaptive refinement (Plot has it). On a
51-term trig sum with PlotPoints -> 100 wolframscript emits 6052 line points
against Woxi's 500. The values agree exactly and the shape is the same; only
small-scale wiggles are under-resolved.
Plot parts nest differently¶
wolframscript wraps curves in GraphicsComplex / Annotation where Woxi emits
plain Line/Arrow lists; SphericalPlot3D samples a different grid;
Graph[…, opts] echoes its options unwrapped where wolframscript wraps them in
a list.
PointSize in PlotStyle and ListPlot's PlotLabel¶
PointSize inside PlotStyle is dropped — the three scatter renderers
hardcode the marker size. ListPlot[…, PlotLabel -> "x"] parses the option but
never draws it (Show[…, PlotLabel -> …] does work).
An AxesLabel carrying a reciprocal power¶
A label's Power[x, -1] renders as x⁻¹ in SVG markup where Wolfram stacks it
as the fraction 1/x.
Graphics3D lighting and projection¶
With the same scene both engines place every primitive identically, but:
- Lighting. Woxi applies a single diffuse term; Wolfram adds specular highlights and coloured light sources, so faces of one polygon come out at noticeably different brightness (its default face colour reads cream where Woxi's reads blue-grey). Woxi also clamps intensity to 1, so a face can never be brighter than its base colour, while Wolfram's ambient plus three directional lights routinely saturate a channel.
- Projection.
ViewAngledrives a perspective camera in Wolfram; Woxi's projection stays effectively orthographic, so a lattice looks uniform instead of splaying toward the viewer. A wide angle (≳30°) leaves the silhouette a few percent narrow.
Compare geometry — vertex positions, which primitives appear, occlusion order — not pixels.
Specularity is accepted and ignored¶
Measured against wolframscript on a sphere under an explicit Lighting spec
matching Woxi's model, Specularity[0.7], Specularity[1] and no
Specularity at all give pixel-identical output. The highlight only appears
under WL's default light set. So a specular term added to Woxi's single-light
model would be invented behaviour; it needs the default lighting first.
SphericalRegion -> True shrinks about 17 % more than Wolfram¶
On a cylinder from {0,0,0} to {10,0,0} at ImageSize -> {550, 350} the
trimmed figure width is 324 px in Wolfram and 268 px in Woxi; without the
option both are 532. Working backwards, Wolfram behaves as if it fit the
enclosing sphere into 399 px of a 550×350 frame — neither dimension, nor their
mean, diagonal or geometric mean. What radius it normalizes by was not decoded.
Texture and VertexTextureCoordinates¶
A textured solid renders as flat-shaded default-blue polygons. Implementing it
means carrying uv pairs per triangle and emitting the image once into <defs>
with per-triangle clip paths and an affine matrix — a renderer feature, and one
that cannot be checked against wolframscript by diffing SVG (it rasterises 3D
output).
Complex* plot functions are unimplemented¶
ComplexPlot, ComplexPlot3D, ComplexListPlot, ComplexArrayPlot,
ComplexContourPlot, ComplexRegionPlot, ComplexVectorPlot and
ComplexStreamPlot are registered as graphics-producing heads (so Head[…]
behaves) but have no implementation; wolframscript renders all of them.
GeoGraphics view and output form¶
The zoom level and single-point default span are Woxi heuristics and will not
match wolframscript's view. GeoGraphics text output is -Graphics- in Woxi
where wolframscript dumps the full GeoGraphics[-Graphics-, GeoBackground -> …,
GeoCenter -> …] options form, and GeoRegionValuePlot returns a bare
Graphics where WL wraps it in Legended.
Geodesic values differ in the last 2–4 digits¶
GeoDistance and friends agree with WL to ~12 significant figures. Wolfram
displays the shortest round-trip of its own geodesic f64, which differs from
geographiclib (both the Rust crate and the canonical Python reference) by a few
to tens of ULP. Not reproducible with this library.
Entity["City", …] resolves to the region centroid, because the offline
country database has no city-level data — Munich resolves to Bavaria's centre
rather than WL's actual Munich coordinates. GeoNearest returns a human name
where WL returns its canonical entity id.
DiskSegment and other region primitives are not drawn¶
DiskSegment has its measures but no graphics rendering.
Buttons inside a picture are not clickable¶
Inset[Button[label, action], pos] inside a Manipulate body draws correctly
and the action is held, but pressing it does nothing: the renderer does not
report each inset button's pixel rectangle, and Woxi Studio has no hit-testing
against the rendered picture.
Manipulate control gaps¶
- A
TabViewof controls is flattened — every tab's controls are shown at once, since Woxi's control panel is one flat list. - One unrecognised control spec makes the entire
Manipulategive up, so a single unsupported control takes every other control with it. Manipulate[…, Initialization :> …]leaks its definitions into the session.
Woxi's own SVG cannot be compared¶
wolframscript's ExportString[…, "SVG"] is always cairo output — pt units,
text as glyph outlines, rgb(%) colours — for every graphics type. A test that
prints raw SVG can never match. Assert the meaning instead (Area[Polygon[…]]
rather than counting <polygon> tags).
Images¶
ImageAdd and friends refuse mismatched dimensions¶
wolframscript -code 'ImageData[ImageAdd[Image[{{0.1, 0.2}}], Image[{{0.3}}]]]'
# {{0.4, 0.2}}
wolframscript -code 'ImageData[ImageAdd[Image[{{0.3}}], Image[{{0.1, 0.2}}]]]'
# {{0.5}}
woxi eval 'ImageData[ImageAdd[Image[{{0.1, 0.2}}], Image[{{0.3}}]]]'
# Error: Evaluation error: ImageAdd: images must have the same dimensions and channels
The result always takes the first image's dimensions, but the second image is
read differently depending on which is larger: a smaller second image is
applied at the top-left and the rest of the first is passed through, while a
larger one contributes its last element (0.3 + 0.2 = 0.5), not its first.
ImageMultiply and ImageSubtract behave the same way.
Refusing is at least honest, but it refuses with a hard error, which aborts the enclosing evaluation.
Four image heads accept a bad specification silently¶
wolframscript -code 'ImageCrop[Image[{{0.1, 0.2}}], x]'
# ImageCrop::arg2: x is not a positive integer, pair of integers, Full or Automatic.
woxi eval 'ImageCrop[Image[{{0.1, 0.2}}], x]'
# ImageCrop[-Image-, x] (no message)
wolframscript -code 'ImagePad[Image[{{0.1, 0.2}}], x]'
# ImagePad::imgpadn: Expecting a number or a 2 by 2 matrix of numbers instead of x.
woxi eval 'ImagePad[Image[{{0.1, 0.2}}], x]'
# ImagePad[-Image-, x] (no message)
wolframscript -code 'TotalVariationFilter[{1, 2, 3, 4, 100}, -1]'
# TotalVariationFilter::arg2: Expecting a non-negative real number, a vector of
# such numbers (for multi-channel images) or Automatic instead of -1.
woxi eval 'TotalVariationFilter[{1, 2, 3, 4, 100}, -1]'
# TotalVariationFilter[{1, 2, 3, 4, 100}, -1] (no message)
These return the right expression and only the message is missing, so they are
conformance gaps rather than wrong answers. Blur and ImageMultiply["x"] are
missing ::bdrad and ::bdarg for the same reason. Note that
TotalVariationFilter's second argument is a regularisation parameter, not a
neighbourhood range, so it does not share the other filters' validation.
More than the documented number of arguments gives ::argt in Woxi where
wolframscript routes through its options machinery and gives nonopt —
systemic across ImageTake, ImagePartition and DistanceTransform.
FindThreshold is unimplemented and its return convention is unclear¶
wolframscript -code 'FindThreshold[Image[{{0., 1.}}]]' # 0.498046875
woxi eval 'FindThreshold[Image[{{0., 1.}}]]'
# FindThreshold[-Image-] is a built-in Wolfram Language function not yet implemented in Woxi.
The Otsu machinery already exists — EdgeDetect uses it, and for the gradient
images it is applied to it reproduces wolframscript exactly: bin into 256 bins
spanning [min, max], maximise the between-cluster variance, and return
min + k (max - min)/256 for the winning bin index k.
What blocks exposing it as FindThreshold is that the reported value is not
always on that grid. FindThreshold[Image[{{0., 1.}}]] is 0.498046875, which
is 127.5/256 — a bin centre, where the gradient cases return a bin edge.
Other probes give 0.4988281324331183 for {{0.2, 0.8}} but an exact 0.25
for {{0.25, 0.5, 0.75}}, so the parity is inconsistent across inputs. This
also blocks the one-argument (Otsu-default) form of MorphologicalBinarize.
EdgeDetect keeps a whole flat gradient plateau¶
wolframscript -code 'ImageData[EdgeDetect[Image[{{0, 0, 1, 1, 0, 0, 1, 1, 0, 0}}]]]'
# {{0, 1, 0, 1, 0, 1, 0, 0, 1, 0}}
woxi eval 'ImageData[EdgeDetect[Image[{{0, 0, 1, 1, 0, 0, 1, 1, 0, 0}}]]]'
# {{0, 1, 0, 1, 1, 1, 1, 0, 1, 0}}
A period-4 square wave is resonant with the default radius-2 kernel and produces a gradient magnitude that is exactly constant across six pixels. wolframscript keeps alternating ones there; Woxi's non-maximum suppression keeps the whole plateau.
No rule over the magnitudes of the two neighbours can tell index 3 from index 4 in that run — both have equal magnitudes on both sides — so the tie-break uses information that could not be identified from the outputs. Every non-degenerate case matches, over 15 reference cases spanning 1D and 2D shapes, colour and Byte images, and explicit thresholds. Not reproducible.
LaplacianGaussianFilter and DerivativeFilter are unimplemented¶
wolframscript -code 'First[ImageData[LaplacianGaussianFilter[Image[{{0., 0., 0., 1., 0., 0., 0.}}], 1]]]'
# {0., 0., 1.8418419230004717, -3.683683846000945, 1.8418419230004717, 0., 0.}
wolframscript -code 'First[ImageData[DerivativeFilter[Image[{{0., 0., 0., 1., 0., 0., 0.}}], {0, 1}]]]'
# {0.05771365940052183, -0.21539030917347252, 0.803847577293368, 0., …}
Both are feature gaps rather than divergences. Neither can reuse
GradientFilter directly: DerivativeFilter normalises its kernel
differently (the impulse responses do not agree up to a scale factor), and
LaplacianGaussianFilter's three-tap response at radius 1 is a scaled second
difference rather than the second-derivative-of-Gaussian kernel the name
suggests.
Colour-space tags do not propagate¶
ImageTake and ImagePartition propagate an image's colour-space tag;
Blur, ImageAdjust, blends, morphological operations and ImageResize all
construct an untagged image. Wolfram probably propagates — each needs its own
probe.
LABColor and its family¶
LABColor is deliberately out of scope; XYZColor, LUVColor and LCHColor
belong to the same family, whose ColorConvert round-trips go through D50/D65
white points and WL-specific matrices that would all need to match bit for bit.
ColorData named gradients¶
ColorData["Rainbow", 0.5] and ColorData["Rainbow"] (a ColorDataFunction)
are unimplemented. ColorData[name, "ColorList"] is
Missing["NotApplicable"] in WL, so the control points are not exposed — they
would have to be recovered by sampling and the interpolation scheme guessed.
The indexed schemes (1, 2, 3) are implemented but scheme 1 agrees to ~15 digits
rather than bit for bit.
ColorQuantize is a stub¶
Echoes unevaluated; a median-cut palette choice would likely be a selection-artifact problem.
ImageMeasurements intensity on colour images¶
WL takes the luma and carries its Real32 rounding (MinIntensity of pure red
is 0.29899999499320984); those measures are left unevaluated in Woxi.
GaussianFilter on an image¶
Differs in the last digits because wolframscript rounds its intermediates
to f32 while Woxi rounds only at the boundaries. wolframscript is also
inconsistent with itself here, giving 0.80123907 and 0.80123901 for the
same kernel tap across evaluation orders.
AudioTrim on tiny objects¶
wolframscript gives {{1., 0.}} for {0., 1., 0.} but {{1.}} for
{1., 0., 1.}, dropping a non-silent sample — minimum-window artifacts at
44.1 kHz, not a rule worth matching. FourierDCT/FourierDST differ in the
last ULP.
Export of a multi-segment Sound¶
Single Play and Audio exports are byte-identical to wolframscript. For
Sound[{Play, Play}] segment 1 matches byte for byte, but WL applies an
unexplained affine requantization to segment 2 (residual ≈ 0.54·s − 0.54 LSB,
uncorrelated with the time shift; not float32 rounding, not floor-versus-round,
not global normalization). Maximum error ≤ 1 LSB of int16.
Not reproducible.
Graphs¶
Wolfram's graph layer is igraph-backed, and two classes of its output are not recoverable from outside.
Selection among equal optima¶
FindVertexCover, FindEdgeCover, FindVertexCut and FindEdgeCut each pick
one minimum solution via igraph internals. Three candidate models
(lexicographically smallest, complement of the lexicographically first or last
maximum independent set, greedy minimum-degree) are each contradicted by
probes: CompleteGraph[4] → {1,2,3}, CycleGraph[6] → {2,4,6} (not
{1,3,5}), GridGraph[{2,3}] → {2,3,6}. FindVertexCut[PathGraph[{1..5}]]
is {4} where {2} and {3} are equally small. Not reproducible.
KnapsackSolve is the same shape: value-optimal solutions are easy, but WL's
choice among ties follows its LinearProgramming simplex/branch-and-bound.
Reverse-index DFS fits some probes and max-count others, and the two
contradict.
Vertex order within a component¶
ConnectedComponents on an undirected graph: Graph[{1<->2, 3<->4}] gives
{{1,2},{3,4}} in Woxi and {{2,1},{4,3}} in WL. WeaklyConnectedComponents
already ships a divergent order for the same reason —
{1→2} lists as {2, 1} but the structurally identical {7→8} lists as
{7, 8}, and {a→b} lists as {a, b}. Every hypothesis tried (original
order, DFS pre/post, BFS from the target, degree-based start) is contradicted.
WeaklyConnectedGraphComponents is unimplemented for this reason.
Not reproducible.
TopologicalSort vertex order¶
WL's vertex ordering is not reproducible by DFS reverse-postorder or by Kahn
with a min/max heap: {1->3, 2->3} → {1,2,3} needs Kahn-min, but
{1->2, 1->3, 2->4, 3->4} → {1,3,2,4} needs DFS, and the two contradict.
Also {1->2, 3->4} → {3,4,1,2}. Woxi always returns the lexicographically
smallest topological order (Kahn's algorithm, ties broken by VertexList
index) — a valid ordering, just not always WL's. Not reproducible.
FindHamiltonianPath choice¶
Graph[{1 <-> 2, 1 <-> 3}] answers {3, 1, 2} while the structurally
identical Graph[{2 <-> 1, 1 <-> 3}] gives {2, 1, 3}. Woxi does a greedy DFS
from the first vertex. Not reproducible.
Provenance-dependent edge order¶
Generator graphs (CycleGraph, CompleteGraph, StarGraph, PetersenGraph)
and explicitly constructed Graph[{…},{…}] objects produce different edge
orderings from the same operations (Subgraph, LineGraph,
NeighborhoodGraph). Woxi implements the generator convention. When a
neighborhood or subgraph covers the whole graph, WL emits igraph's BFS
traversal order.
GroupCentralizer generator order¶
The returned generators are almost the structural centralizer generators, but
the ordering contradicts itself across cases — S4/(12)(34) gives rotations
ascending while S6/(12)(34)(56) reverses the first pair, and fixed points use
descending adjacent transpositions unlike the 2-cycle classes' star swaps.
It is Schreier–Sims discovery order. Not reproducible.
VertexCoordinates annotations¶
An item-level VertexCoordinates read returns the laid-out coordinate, so
Woxi answers from its own GraphEmbedding — the values diverge because the
layout differs.
Multi-entry rule lists inside a graph option¶
WL scrambles the order of a multi-entry rule list inside an option
(VertexLabels -> {2 -> "b", 1 -> "a"} for the input {1 -> "a", 2 -> "b"}) —
internal hash order. Woxi keeps input order.
Geometry and regions¶
TriangleCenter accepts a triangle embedded in 3D¶
TriangleCenter[Triangle[{{0,0,0},{4,0,0},{0,3,0}}], "Circumcenter"] is
{2, 3/2, 0} in Woxi; wolframscript handles only the planar case and leaves
the call unevaluated. Deliberate — the 3D centre is well defined.
ConvexHullMesh is unevaluated for 3D point sets¶
wolframscript -code 'ToString[ConvexHullMesh[{{0,0,0},{1,0,0},{0,1,0},{0,0,1}}], InputForm]'
# BoundaryMeshRegion[{{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}},
# {Polygon[{{3, 2, 1}, {2, 4, 1}, {4, 3, 1}, {3, 4, 2}}]},
# Method -> {"SeparateBoundaries" -> False}, WorkingPrecision -> Infinity]
woxi eval 'ConvexHullMesh[{{0,0,0},{1,0,0},{0,1,0},{0,0,1}}]'
# ConvexHullMesh[{{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}}]
Computing the hull is the easy part; WL hands qhull's internal facet
bookkeeping straight through, and three things would have to be replicated to
match the printed Polygon:
- Facet order. No sort explains all three samples below.
- Vertex rotation within a face. Faces are outward-oriented, but the starting vertex varies.
- Coplanar merging. Triangles that share a plane come back as one polygon, so the cube's six faces are quads, not twelve triangles.
Reference outputs (all carry Method -> {"SeparateBoundaries" -> False},
WorkingPrecision -> Infinity):
| points | faces |
|---|---|
{{0,0,0},{1,0,0},{0,1,0},{0,0,1}} |
{{3,2,1},{2,4,1},{4,3,1},{3,4,2}} |
the same plus {1,1,1} |
{{3,2,1},{2,4,1},{4,3,1},{3,5,2},{5,4,2},{4,5,3}} |
| the eight unit-cube corners | {{3,2,1,4},{1,2,6,5},{4,1,5,8},{2,3,7,6},{3,4,8,7},{5,6,7,8}} |
Mesh cell order is qhull's¶
Which vertex a triangle starts at, and the order of the cells, follow qhull —
{{1,2,3},{3,2,4}} against Woxi's {{1,2,3},{2,4,3}}, the same two triangles
with the same orientation. Compare with Sort[Sort /@ MeshCells[m, 2][[All,1]]].
Wolfram is also inconsistent with itself here:
Perimeter[DelaunayMesh[…]] of an exact square is 8. while Woxi gives the
exact 8. RegionMeasure[Polygon, spec] in WL is a machine-precision quadrature
value (3.366666189468263 for the exact 101/30) while its
MomentOfInertia[Polygon] is exact — so Woxi leaves polygon RegionMoment
unevaluated on purpose.
Symbolic region membership¶
RegionMember with a symbolic point is unevaluated for HalfSpace,
StadiumShape and others. WL emits a Reduce-normalized condition
Element[x | y, Reals] && <inequality> with non-trivial canonicalization:
rational bounds cleared to integers, division by the coefficient gcd,
negative-coefficient terms moved across, and an all-negative normal flipped to
a >= 0 shape. Reproducing it is a mini-Reduce.
Region scope gaps¶
RegionDisjointleaves ellipseDisk[c, {a, b}](a quartic distance problem),ImplicitRegion,AnnulusandLineunevaluated.RegionMomentfor an ellipseDiskis open, as isMomentOfInertiabeyond what is implemented.- Simplices in a higher embedding dimension than their intrinsic dimension
(a triangle given as three points in 3-space) need the Gram determinant and
are unevaluated. The no-argument regular
Tetrahedron[]/Simplex[n]defaults are only partly handled. RegionBoundsandRegionMemberforAnnulusare unimplemented.SphericalShellandCapsuleShapeSurfaceAreawith symbolic radii is left unevaluated, because wolframscript itself hangs on it.RegionMeasure[ImplicitRegion[…]]is hard in general.
PolyhedronData form divergences¶
Two entries in the hand-written Dodecahedron record print differently:
Sqrt[5/8 + (11*Sqrt[5])/40] against WL's Sqrt[5/8 + 11/(8*Sqrt[5])], and a
Sqrt[…] where WL gives Root[1 - 20*#1^2 + 80*#1^4 &, 3, 0]. Re-evaluating
the first flips it to Wolfram's, so === against the stored value can be False
for the same value depending on how many evaluation passes each side saw.
Molecule residues¶
[13C]-style isotope atoms get a WL UnpairedElectronCount property Woxi
omits, and the no-carbon Hill ordering diverges — WL writes ammonium as NH4
(N before H) where Woxi keeps strict alphabetical order.
SpherePoints and ShiftRegisterSequence¶
SpherePoints[n] returns specific polyhedral configurations for small n, but
the machine values leak WL's construction pipeline (SpherePoints[4] starts
0.9999999999999999), and large n switch to an undocumented regularized
spiral. ShiftRegisterSequence's explicit-taps form contradicts its own
default-form model: {4, {3,4}}, {5, {2,5}} and {5, {3,5}} all return
{0} even though those polynomials are primitive, yet the n=4 default
satisfies exactly the {3,4} recurrence. Not reproducible.
IsolatingInterval¶
No decodable convention: Sqrt[2] → {45/32, 91/64} (width 2^-6), Sqrt[3] →
width 2^-5, but 2^(1/3) and Root[#^5-#-1&,1] → {0, 2} (unrefined), and
Sqrt[2]+Sqrt[3] → {1, 4} (width 3, not from bisection at all). The
refinement depth comes from WL's internal interval-arithmetic convergence loop.
Not reproducible.
JuliaSetIterationCount¶
Escaping orbits fit R = 2 + |c|² over four probes, and an undecided orbit
gives cap+1. But bounded orbits return small counts via an attractor/cycle
detection with internal tolerances — [0, 1/2] → 3, [0, 0.9] → 5,
[0, 0.31] → 2, yet the parabolic [0.25, 0.1] → 1001. A fixed inner
threshold needs ε ∈ (0.0343, 0.0625]; successive-difference and
Brent-checkpoint models both fail. MandelbrotSetIterationCount was decodable
and is implemented. Not reproducible.
FindClusters without an explicit cluster count¶
FindClusters[{1,2,10,11,20,21}] gives {{1,2,10,11},{20,21}} in Woxi and
{{1,2},{20,21},{10,11}} in WL — a different k and a non-sorted emission
order, both from an internal silhouette/gap heuristic. The explicit-k and
DistanceFunction forms are correct. Not reproducible.
FindPeaks with a non-zero smoothing scale¶
FindPeaks[list, σ] is not FindPeaks[GaussianFilter[list, σ]]: at σ=2.2 the
blurred list still has two peaks but FindPeaks reports one, and no blur radius
reconciles the counts across σ. It is a scale-space tracking pass. A bare
sharpness inherits σ as its scale and is not the plain second difference of the
blurred data either. Woxi leaves any non-zero scale unevaluated.
Astronomy residues¶
SunPosition, MoonPosition and SiderealTime now agree closely, but:
- The Sun's near-meridian azimuth differs by ~20″, because Meeus's short solar series is only good to 0.01°.
Sunrise/Sunsetstay 13–30 s away: the half-day length implies wolframscript uses an effective horizon altitude near −0.885°, not the standard −50′ that Meeus documents. The offset is systematic across latitudes and seasons but has no physical derivation. Do not tune the constant to fit it.SiderealTimeis 0.28 s off (a different nutation/precession model).
Import, export, units and system¶
ExampleData bundles its own data and properties¶
Woxi ships the "NetworkGraph" datasets it assembled from the original
sources rather than Wolfram's catalogue, and exposes the properties that data
supports — "VertexList", "EdgeRules", "AdjacencyMatrix" — none of which
wolframscript knows (it answers ExampleData::notpropx). Its own list is
ByteCount, Description, EdgeCount, EdgeProperty, FullGraph, Graph,
LongDescription, Name, Source, StandardName, VertexCount, VertexProperty.
Deliberate: the catalogue is Wolfram's. Write tests against shape and
presence, never against either side's catalogue.
ShortTimeFourier partitions differently¶
Woxi's default partition offset is half the window; wolframscript's is 1, so
the same signal yields 3 frames against its 8. The property names differ too:
Woxi answers "WindowSize", "Offset" and "NumberOfFrames" where
wolframscript's ShortTimeFourierData has "PartitionSize",
"PartitionOffset", "SmoothingWindow", "PartitionPadding",
"FourierParameters", "SampleRate" and "DataType".
UnitSimplify is unimplemented¶
Two parts. The clean one: a compound dimension matching a named SI derived unit
becomes that unit (N*m → Joules, kg*m/s^2 → Newtons, J/s → Watts), while
metric units and no-named-unit compounds stay (1/s stays 1/s, not Hertz).
The rabbit hole: a single imperial unit converts to a magnitude-dependent
metric unit — Feet → 762/25 Centimeters, Miles → 25146/15625 Kilometers —
and the metric-prefix choice (cm vs m vs km) is WL-specific and unpredictable.
Implementing only the clean part would still diverge on the common cases.
Temperature arithmetic is not affine¶
| expression | wolframscript | Woxi |
|---|---|---|
Quantity[5,"Celsius"] + Quantity[3,"Celsius"] |
Quantity[5543/10, Kelvins] |
Quantity[8, DegreesCelsius] |
Quantity[10,"Celsius"] - Quantity[3,"Celsius"] |
Quantity[7, DegreesCelsiusDifference] |
Quantity[7, DegreesCelsius] |
Quantity[5,"Celsius"] + Quantity[3,"Kelvins"] |
Quantity[8, DegreesCelsius] |
a compat error |
2 Quantity[5,"Celsius"] |
Quantity[5563/10, Kelvins] |
Quantity[10, DegreesCelsius] |
Needs a full absolute-versus-difference temperature model with the
DegreesCelsiusDifference / DegreesFahrenheitDifference units.
MixedRadixQuantity is unimplemented¶
Needs MixedMagnitude and MixedUnit first.
WXF serialization bails¶
BinarySerialize deliberately leaves these unevaluated rather than write bytes
that would differ: a Plus/Times containing a numeric-complex term (WL sorts
Complex first in sums and folds numeric factors into it in products),
Pattern/Function/Slot expression kinds, BigFloat, and multi-operator
comparison chains. The deserializer treats the NumericArray token as corrupt,
since Woxi has no such object. BinarySerialize[Range[3]] is packed in WL —
only literal lists are comparable.
CSV ColumnTypes¶
wolframscript types the last column of a labelled table as "String" when the
text does not end in a newline, contradicting its own import of the same field
as an integer. It also changes its CSV behaviour mid-session once any "Table"
import has run — HeaderLines is ignored in a cold kernel and honoured after,
and booleans are coerced in a cold kernel and left as strings after. Woxi
implements the cold-kernel behaviour and stays newline-agnostic.
Not reproducible.
Its ColumnTypes values are self-contradictory in the labelled two-column
case as well: "a,b\n1,2" types the last column as String although it holds
numbers, while "a,b,c\n1,2,3" types all three Integer64.
Import element lists¶
WL's element list for CSV names EventSeries and TimeSeries, which Woxi does
not implement.
Named characters and Unicode drift¶
wolframscript's Unicode database is older than Rust's. A sweep of
CharacterNormalize over the BMP and SMP found zero cases where
wolframscript normalizes and Woxi disagrees — every difference is the reverse,
Woxi normalizing a character wolframscript does not know yet (Latin Extended-D
additions, Vithkuqi, Latin Extended-F, Todhri, ㋿). Woxi's output is the
standards-correct one. Never write a conformance test on a code point added
after Unicode ~13.
Note also that wolframscript stores most named characters at private-use
code points (\[Rule] is U+F522, \[WarningSign] U+F725), and mangles
non-ASCII input in both -code and -file — several apparent divergences in
this area are artifacts of that.
SeedRandom streams differ¶
Wolfram uses a proprietary generator, Woxi uses ChaCha8; no seed produces the same sequence. Verify determinism and distribution instead. Not reproducible.
Performance¶
Woxi's list operations are bound by expression-allocation throughput, not by
algorithmic complexity: Expr::List holds an owned vector with no structural
sharing, so building a large result deep-copies its elements.
Tuples[{Range[4900], Range[70]}] (343k pairs) takes ~1.5 s in release —
roughly 100× slower than wolframscript, which shares structure. A real fix
needs Rc/Arc-backed lists or a different allocator.
woxi eval exits 0 on an evaluation error¶
The error goes to stderr but the exit code stays 0, so a shell check like
woxi eval '…' >/dev/null 2>&1 && echo OK reports OK even when the expression
failed. (wolframscript -file has the same property.)
balanced_ternary computes a wrong value¶
btmultiply["+-0++0+", btsubtract[tobt@-436, "+-++-"]] gives +--0 in Woxi
where wolframscript gives ----0+--0++0. The divergence is inside btadd's
Fold over a transposed, padded character matrix combined with a multi-clause
StringReplace over alternative patterns — a pattern-matching or accumulator
bug, not the (already fixed) #0@ self-reference.