Ask Your Question
3

Is this a bug in the log function?

asked 2025-09-23 11:25:47 +0200

Sean Eberhard gravatar image

updated 2025-09-24 17:40:08 +0200

slelievre gravatar image

While doing an experiment I had an anomalous result and traced it to the following unexpected output of log(x, 2). Can anybody explain what is going on here?

┌────────────────────────────────────────────────────────────────────┐
│ SageMath version 10.7, Release Date: 2025-08-09                    │
│ Using Python 3.12.5. Type "help()" for help.                       │
└────────────────────────────────────────────────────────────────────┘
sage: x = int(21743271936)
sage: factor(x)
2^28 * 3^4
sage: log(x, 2).n()
28.0000000000000
sage: log(x-1, 2).n()
34.3398500028183
sage: log(x+1, 2).n()
34.3398500029510

edit retag flag offensive close merge delete

Comments

Welcome to Ask Sage!

slelievre gravatar imageslelievre ( 2025-09-23 15:29:02 +0200 )edit

2 Answers

Sort by » oldest newest most voted
3

answered 2025-09-23 17:11:32 +0200

slelievre gravatar image

updated 2025-09-23 22:33:07 +0200

Yes, this is a bug.

It seems log(int(n), 2) fails for a specific set of values of n.

Take a, b, k with 2 <= a <= 30, a + b = 32, k >= 1.

Then log(int(2^a * (k * 2^b + 1)), 2) incorrectly returns a.

Smallest case: 2^2 * (2^30 + 1) ie 4294967300.

If n is an int, calling log(n, 2) calls logb(n, 2).

So the following illustrates the bug for each (a, b) and the first few k.

sage: from sage.functions.log import logb
sage: n_py = lambda a, b: int(2)**a * int(b)
sage: n_zz = lambda a, b: ZZ(2)**a * ZZ(b)
sage: f_py = lambda a, b: logb(n_py(a, b), 2)
sage: f_zz = lambda a, b: logb(n_zz(a, b), 2)
sage: mismatch = lambda a, b: f_py(a, b) != f_zz(a, b)
sage: aa = range(2, 31)
sage: kk = range(1, 4)
sage: ab = [(a, k * c + 1) for a in aa for c in [2**(32 - a)] for k in kk)
sage: all(mismatch(a, b) for a, b in ab)
True
sage: min((2**a * b, (a, b)) for a, b in ab)
(4294967300, (2, 1073741825))

Similar past bug: https://trac.sagemath.org/ticket/25979/,
moved to https://github.com/sagemath/sage/issues/25979.

edit flag offensive delete link more

Comments

2

I opened a bug report here: https://github.com/sagemath/sage/issu...

Sean Eberhard gravatar imageSean Eberhard ( 2025-09-25 11:36:45 +0200 )edit
0

answered 2025-09-29 15:11:22 +0200

dan_fulea gravatar image

The implemented method log tries to guess the intention, and sometimes this intention is not clear. In the given case we are mixing the following two things: - log is a sage-specific function, it is not the native python log-function, which is implemented as math.log after importing math. Going the python way, this should work. - x = int(mynumber) insures that x is an instance of the python class int, which comes with its obvious, wanted arithmetic limitations when used.

Mixing sage and python is in general not a good idea. And while working with the sage interpreter it is so simple to stay in the sage world. Doing so there is no problem:

sage: x = 21743271936
sage: log(x, 2).n()
34.3398500028846
sage: type(x)
<class 'sage.rings.integer.Integer'>

But making x intentionally an int instance makes the sage log switch to a guessing path.

I've typed for instance ??log into the sage interpreter, and this gives the whole information to the log function. At the end:

    base = kwds.pop('base', None)
    if base is not None:
        args = args + (base,)
    if not args:
        raise TypeError("log takes at least 1 arguments (0 given)")
    if len(args) == 1:
        from sage.functions.log import ln
        return ln(args[0], **kwds)
    if len(args) > 2:
        raise TypeError("log takes at most 2 arguments (%s given)" % (len(args) + 1 - (base is not None)))
    try:
        return args[0].log(args[1])
    except ValueError as ex:
        if ex.args[0].startswith("no logarithm"):
            raise
    except (AttributeError, TypeError):
        pass
    from sage.functions.log import logb
    return logb(args[0], args[1])
File:      /usr/lib/python3.13/site-packages/sage/misc/functional.py
Type:      function

And the road log(21743271936, 2).n() is not taken. But which is the road taken. We start by setting the base two. We have two arguments, so we are trying to evaluate x.log(2), and probably sage took a lot of time to insure log is a present method for all classes that may somehow make sense to be joined with a log method. For instance:

sage: 21743271936.log(2)
log(21743271936)/log(2)
sage: _.n()
34.3398500028846
sage: type(21743271936)
<class 'sage.rings.integer.Integer'>

(In particular, the last mentioned class comes with a log.)

But our int-instance is an instance of a basic (arithmetically and mathematically poor) class. So we go into the final default case, we import logb, and ask for logb applied on our int-object w.r.t. the base two. What is logb? After ??logb we see that the code goes into a GinacFunction.

Note that inside /usr/lib/python3.13/site-packages/sage/functions/log.py we have the line logb = Function_log2(). So we obtain the information to the class Function_log2.

Signature:      logb(self, coerce=True, hold=False, dont_call_method_on_arg=False, *args)
Type:           Function_log2
String form:    log
File:           /usr/lib/python3.13/site-packages/sage/functions/log.py
Source:
class Function_log2(GinacFunction):
    """
    Return the logarithm of x to the given base.

    See :meth:`log() <sage.functions.log.log>` for extensive documentation.

    EXAMPLES::

        sage: from sage.functions.log import logb
        sage: logb(1000, 10)                                                            # needs sage.symbolic
        3

    TESTS::

        sage: logb(7, 2)                                                                # needs sage.symbolic
        log(7)/log(2)
        sage: logb(int(7), 2)                                                           # needs sage.symbolic
        log(7)/log(2)
    """
    def __init__(self):
        """
        TESTS::

            sage: from sage.functions.log import logb
            sage: loads(dumps(logb))
            log
        """
        GinacFunction.__init__(self, 'log', ginac_name='logb', nargs=2,
                               latex_name=r'\log',
                               conversions=dict(maxima='log'))

And indeed, we reproduce the error.

sage: logb(x, 2)
28

What happens inside this class? Hard to say, it there is a pxd function file only:

[dan@k9 ~]$ ll /usr/lib/python3.13/site-packages/sage/symbolic/function*
-rwxr-xr-x 1 root root 281K 25. Aug 11:53 /usr/lib/python3.13/site-packages/sage/symbolic/function.cpython-313-x86_64-linux-gnu.so*
-rw-r--r-- 1 root root  13K 25. Aug 11:53 /usr/lib/python3.13/site-packages/sage/symbolic/function_factory.py
-rw-r--r-- 1 root root  793 25. Aug 11:53 /usr/lib/python3.13/site-packages/sage/symbolic/function.pxd

So the error is in the black box.

edit flag offensive delete link more

Comments

Please add your analysis to https://github.com/sagemath/sage/issu...

Max Alekseyev gravatar imageMax Alekseyev ( 2025-09-29 17:45:09 +0200 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

Stats

Asked: 2025-09-23 11:25:47 +0200

Seen: 3,715 times

Last updated: 22 hours ago