Ask Your Question
1

can one now set a timelimit on operation in sagemath?

asked 2020-03-23 14:22:35 -0500

Nasser gravatar image

updated 2020-03-24 05:07:28 -0500

My question here is this: Can now (version 9) tell sagemath to set a timeout on some call and have sagemath issue an error (may be exception) if timeout expires and the call have not completed yet? I am mainly interested in integrate calls, which some take long time.

Here is an example of how this is done in Maple limits the amount of CPU time spent on a computation

restart; 
integrand:=(b*x + a)^(3/2)*(d*x + c)^(5/2)/x^7;
try
    timelimit(300,int(integrand,x));
    print("Finished before time out, good");
catch:
    print("opps, timed out");
end try;

Is it possible to do the above directly in sagemath without me having to program the timelimit myself using Process and Queues? I am using sagemath 9 on Linux.

update

I am getting stack dump when implementing the alarm method shown in the answer below. I am not sure why that is. May be I am making an error somewhere. Below is a MWE to reproduce it.

I created a file build_fricas_new_timeout_one_integral.sage with content

#!/usr/bin/env sage

from sage.all import *
from cysignals.alarm import alarm, AlarmInterrupt, cancel_alarm

var('x a b')

def doTheIntegration():
    integrand = tan(x)/(a^3+b^3*tan(x)^2)^(1/3)

    fricas.setSimplifyDenomsFlag(fricas.true)
    anti=integrate(integrand,x,algorithm="fricas")                        
    return anti

try:
    alarm(20)        
    anti = doTheIntegration()        
except AlarmInterrupt:      
    print("Timed out")
else:
    print("Completed OK, anti=",anti)
    cancel_alarm()

Then called it as follows sage ./build_fricas_new_timeout_one_integral.sage then I get this on the screen

>sage ./build_fricas_new_timeout_one_integral.sage 
Interrupting FriCAS...
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/sage/interfaces/expect.py", line 986, in _eval_line
    E.expect(self._prompt)
  File "/usr/lib/python3.8/site-packages/pexpect/spawnbase.py", line 343, in expect
    return self.expect_list(compiled_pattern_list,
  File "/usr/lib/python3.8/site-packages/pexpect/spawnbase.py", line 372, in expect_list
    return exp.expect_loop(timeout)
  File "/usr/lib/python3.8/site-packages/pexpect/expect.py", line 169, in expect_loop
    incoming = spawn.read_nonblocking(spawn.maxread, timeout)
  File "/usr/lib/python3.8/site-packages/pexpect/pty_spawn.py", line 500, in read_nonblocking
    if (timeout != 0) and select(timeout):
  File "/usr/lib/python3.8/site-packages/pexpect/pty_spawn.py", line 450, in select
    return select_ignore_interrupts([self.child_fd], [], [], timeout)[0]
  File "/usr/lib/python3.8/site-packages/pexpect/utils.py", line 143, in select_ignore_interrupts
    return select.select(iwtd, owtd, ewtd, timeout)
  File "src/cysignals/signals.pyx", line 320, in cysignals.signals.python_check_interrupt
cysignals.signals.AlarmInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./build_fricas_new_timeout_one_integral.sage.py", line 22, in <module>
    anti = doTheIntegration()        
  File "./build_fricas_new_timeout_one_integral.sage.py", line 17, in doTheIntegration
    anti=integrate(integrand,x,algorithm="fricas")                        
  File "/usr/lib/python3.8/site-packages/sage/misc/functional.py", line 753, in integral
    return x.integral(*args, **kwds)
  File "sage/symbolic/expression.pyx", line 12391, in sage.symbolic.expression.Expression.integral (build/cythonized/sage/symbolic/expression.cpp:64575)
  File "/usr/lib/python3.8/site-packages/sage/symbolic/integration/integral.py", line 927, in integrate
    return integrator(expression, v, a, b)
  File "/usr/lib/python3.8/site-packages/sage/symbolic/integration/external.py", line 386, in fricas_integrator
    result = ex.integrate(v)
  File "/usr/lib/python3.8/site-packages/sage/interfaces/interface.py", line 680, in __call__
    return self._obj.parent().function_call(self._name, [self._obj] + list(args), kwds)
  File "/usr/lib/python3.8/site-packages/sage/interfaces/interface.py", line 601, in function_call
    return self.new(s)
  File "/usr/lib/python3.8/site-packages/sage/interfaces/interface.py", line 370, in new
    return self(code)
  File "/usr/lib/python3.8/site-packages/sage/interfaces/interface.py", line 296, in __call__
    return cls(self, x, name=name)
  File "/usr/lib/python3.8/site-packages/sage/interfaces/expect.py", line 1471, in __init__
    self._name = parent._create(value, name=name)
  File "/usr/lib/python3.8/site-packages/sage/interfaces/interface.py", line 501, in _create
    self.set(name, value)
  File "/usr/lib/python3.8/site-packages/sage/interfaces/fricas.py", line 589, in set
    output = self.eval(cmd, reformat=False)
  File "/usr/lib/python3.8/site-packages/sage/interfaces/fricas.py", line 847, in eval
    output = Expect.eval(self, code, strip=strip,
  File "/usr/lib/python3.8/site-packages/sage/interfaces/expect.py", line 1384, in eval
    return '\n'.join([self._eval_line(L, allow_use_file=allow_use_file, **kwds)
  File "/usr/lib/python3.8/site-packages/sage/interfaces/expect.py", line 1384, in <listcomp>
    return '\n'.join([self._eval_line(L, allow_use_file=allow_use_file, **kwds)
  File "/usr/lib/python3.8/site-packages/sage/interfaces/expect.py", line 1017, in _eval_line
    self._keyboard_interrupt()
  File "/usr/lib/python3.8/site-packages/sage/interfaces/expect.py", line 1039, in _keyboard_interrupt
    raise KeyboardInterrupt("Ctrl-c pressed while running %s" % self)
KeyboardInterrupt: Ctrl-c pressed while running FriCAS
>

Am I doing something wrong in using the alarm method?

edit retag flag offensive close merge delete

1 answer

Sort by » oldest newest most voted
2

answered 2020-03-24 02:57:00 -0500

Sébastien gravatar image

updated 2020-03-24 12:46:26 -0500

One way is to use alarm and AlarmInerrupt:

sage: from cysignals.alarm import alarm, AlarmInterrupt, cancel_alarm

If computation takes more than 5 seconds, computation is stopped:

sage: try:
....:     alarm(5)
....:     factor(2^1000-1)
....: except AlarmInterrupt:
....:     print('opps, timed out!')
....: else:
....:     cancel_alarm()
....:     
opps, timed out!

If computation takes less than 5 seconds, it is not stopped:

sage: try:
....:     alarm(5)
....:     factor(2^100-1)
....: except AlarmInterrupt:
....:     print('opps, timed out!')
....: else:
....:     cancel_alarm()
....:     
3 * 5^3 * 11 * 31 * 41 * 101 * 251 * 601 * 1801 * 4051 * 8101 * 268501

It is important to cancel the alarm if the computation succeeds (as done above in the else clause) or otherwise the AlarmInterrupt will be raised later on.

EDIT: In some cases including the question asked here, you may need to also catch KeyboardInterrupt exception:

#!/usr/bin/env sage

from sage.all import *
from cysignals.alarm import alarm, AlarmInterrupt, cancel_alarm

var('x a b')

def doTheIntegration():
    integrand = tan(x)/(a^3+b^3*tan(x)^2)^(1/3)

    fricas.setSimplifyDenomsFlag(fricas.true)
    anti=integrate(integrand,x,algorithm="fricas")                        
    return anti

try:
    alarm(20)        
    anti = doTheIntegration()        
except (AlarmInterrupt,KeyboardInterrupt):      
    print("Timed out")
else:
    print("Completed OK, anti=",anti)
    cancel_alarm()
edit flag offensive delete link more

Comments

Thanks, but when testing this method I found a problem calling integrate. I get During handling of the above exception, another exception occurred:. since space here is too small, I will now update this in my question to give an example how to reproduce it.

Nasser gravatar imageNasser ( 2020-03-24 04:41:41 -0500 )edit
1

I would not trust the results of a sage session that regularly has been interrupted (by alarms or user interrupts): while the code generally tries to block interrupts in critical parts and then deal with them once the system is in a more consistent state, I would expect sage doesn't entirely succeed: I expect in some cases the system will be left in an inconsistent state (there are bugs, so this is also true without interrupts...)

If you want to time-limit computations "safely", I would trust a fork-and-kill method more: then you at least discard the state of the computing processif it didn't complete. I think that indeed needs you to put some plumbing in place yourself, but that could be wrapped up in convenient routines.

nbruin gravatar imagenbruin ( 2020-03-24 11:10:57 -0500 )edit
1

I updated my question to fix the issue when running with fricas code (I think fricas catches the AlarmInterrupt and replaces it by a KeyboardInterrupt).

Sébastien gravatar imageSébastien ( 2020-03-24 12:48:14 -0500 )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

1 follower

Stats

Asked: 2020-03-23 14:22:35 -0500

Seen: 58 times

Last updated: Mar 24