Re: ezunits fundamental_units quite slow

Michael Soegtrop via Maxima-discuss <[email protected]> Sun, 17 May 2026 12:33:12 +0200
Newsgroups gmane.comp.mathematics.maxima.general
Message-ID <[email protected]>
Dear Robert,

looking at the fundamental_units code, I think the fastest way is to use 
assoc with conversions_to_base_units and to recurse through the term 
structure of the unit term. Not super nice but the version below is 
about 70x faster and I believe functionally equivalent.

Btw.: if one would replace " if numberp(expr) then 1" with " if 
numberp(expr) then expr" or not recurse on unit results, this would 
return the conversion factor as well - which would be a useful variant 
for a convert to base units function, but one would need it separately.

And one more note: 95% of the time goes into stripping the numeric 
factor. I couldn't find a (much) faster way of doing this. A separate 
function applied in the end is 1.5x faster, but that is as far as I got. 
But if the number stripping is left away (don't recurse and return 
expr), I am at 2000x faster. I believe there must be a fast way to strip 
the numeric factor out of results which is faster, but I couldn't find 
one (without lisp - even ?cons ?car ?cdr work in Maxima did not help me, 
nor did simp:false in the function block). Maybe you have an idea how to 
do this.

Best regards,

Michael


/* Recursive tree-walk: expand a unit expression to base units
  * using the pre-computed conversions_to_base_units list.       */
fundamental_units_rec(expr) := block([r],
     if atom(expr) then (
         if numberp(expr) then 1
         else (
             r : assoc(expr, conversions_to_base_units),
             if r # false
             then fundamental_units_rec(r) /* recurse to strip numeric 
factors */
             else expr                     /* base unit: return as-is */
         )
     )
     else if op(expr) = "*" then map('fundamental_units_rec, expr)
     else if op(expr) = "^" then fundamental_units_rec(first(expr)) ^ 
second(expr)
     else if op(expr) = "/" then fundamental_units_rec(num(expr)) / 
fundamental_units_rec(denom(expr))
     else 'fundamental_units(expr) /* unknown form: return noun */
)$

fundamental_units([L]) :=
     if L = []
         /* No-arg form: return list of base unit symbols [kg, m, s, ...] */
     then
         map(
             lambda([x], r : get(x, 'unit), if r = false then 'unit(x) 
else r),
             fundamental_dimensions
         )
     else if listp(L[1])
         /* List argument: apply elementwise */
         then map('fundamental_units, L[1])
     else (
         /* Ensure conversions_to_base_units has been computed */
         if conversions_to_base_units = 'conversions_to_base_units
             then compute_conversions_to_base_units(),
         if unitp (L[1])
         then
             /* Backtick expression like (1`J): strip quantity, expand 
unit part */
             fundamental_units_rec(units(L[1]))
         else
             /* Bare unit or compound expression like J, N*m, J/s^2 */
             fundamental_units_rec(L[1])
     )$


_______________________________________________
Maxima-discuss mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/maxima-discuss