How to ask the user for legal inputs?

Suppose we have the following code for the Taylor expansion.

 @interact
def Taylor(
f = input_box(default=e^x ),
n = slider(vmin=0, vmax=10, step_size=1, default=3, label="Select the order n: ")):
print (f, " = " , f.taylor(x, 0, n))


Then we will get a Sage Interact. We can change the function e^x to any function of x and get the Taylor expansion.

However if we input something like e^y, we will get an error from Sage.

Is there some way that we can modify the code so that if someone input something which is not a function of x, it will print "illegal input" instead of returning an error?

More generally, if I require the input to be an element of a polynomial ring, for example Q[x,y,z], then how to ask Sage to print "illegal input" if someone input something like t^2.

edit retag close merge delete

Sort by » oldest newest most voted

Concerning the first question, you could try the following code:

var("x")
@interact
def Taylor(
f = input_box(default="e^x", type=str),
n = slider(vmin=0, vmax=10, step_size=1,
default=3, label="Select the order n: ")):

try:
f = SR(f)
if x not in f.variables():
print("Illegal input. Try again")
else:
print("Taylor expansion:\n")
print(f, " = " , f.series(x==0, n))
except TypeError:
print("Please, finish typing a valid function")


See this SageMath Cell.

The first problem we have to face arises when SageMath reads the contents of the input box. In a Jupyter notebook, this happens many times per second while you type; in a SageMath Cell, just after hiting the Enter key. Without the type=str option in input_box, SageMath expects f to be an expression, so it can raise several kinds of errors, such as SyntaxError or NameError exceptions, for example, if the input is e^x+ (due to an unexpected EOF while parsing) or e^x+y (if y has not been defined as a variable). The option type=str avoids these errors, since f is just a string.

Now we can type a string without annoying error messages (well, still not; see below). The string is stored in f. But, before computing any series expansion, we have to convert f to a symbolic expression, done in the line f = SR(f), and check if x is a variable present in f. Hence, something like sin(a*x) is allowed, whereas t^2 just yields the error message «Illegal input…». If f contains a valid function, the Taylor expasion is obtained and printed. Then, in any case, the user can modify the expression or type a new one.

Why the main code is inside a try/except block? Suppose you want the expansion of e^(x^2). While you type, the content of the input box is processed. So, after writing, say e^(x^, SageMath would raise a TypeError exception, since e^(x^ is a malformed expression. Thanks to the the try/except commands, we can catch these errors and print a message to invite the user to finish typing.

Finally, I have replaced the taylor method by the series one, since, in general, f is not equal to the Taylor polynomial.

Concerning the second question, you can use similar code, like the following one:

R.<x,y,z> = PolynomialRing(QQ)

@interact
def Taylor(
f = input_box(default="x+y+z", type=str)):
try:
f = SR(f)
if f not in R:
print("Illegal input. Try again")
else:
print("The polynomial belongs to R")
except TypeError:


See this SageMath Cell

EDIT.

According to Sébastien comment, it is better to move part of the code to an else clause. So, here we have a reformulation of the code:

var("x")
@interact
def Taylor(
f = input_box(default="e^x", type=str),
n = slider(vmin=0, vmax=10, step_size=1,
default=3, label="Select the order n: ")):
try:
f = SR(f)
except TypeError:
print("Please, finish typing a valid function")
else:
if x not in f.variables():
print("Illegal input. Try again")
else:
print("Taylor expansion:\n")
print(f, " = " , f.series(x==0, n))


And also, for the second script:

R.<x,y,z> = PolynomialRing(QQ)

@interact
def Taylor(
f = input_box(default="x+y+z", type=str)):
try:
f = SR(f)
except TypeError:
else:
if f not in R:
print("Illegal input. Try again")
else:
print("The polynomial belongs to R")

more

1

I think it is a good habit to keep only the strict minimum inside the try, i.e., the line f = SR(f). In general, other unrelated TypeError can be raised by the rest of the code inside of the try and be catched which is not desirable.

( 2020-04-26 02:51:51 -0600 )edit

I agree. But, how would you restructure the code to achieve that in this case?

( 2020-04-26 07:54:49 -0600 )edit

( 2020-04-26 09:25:31 -0600 )edit

There is a way to autodeclare symbolic variables when defining a symbolic expression from a string.

Use symbolic_expression(f) to turn the string f into a symbolic expression this way. See

( 2020-04-27 08:02:37 -0600 )edit

If you know the kind of errors that are raised by the wrong input (for example AttributeError and ValueError, a complete list of exceptions can be found in the docs of python), you can catch those errors and return or print an error message.

try:
result = f.taylor(x,0,n)
except (AttributeError, ValueError):
print('illegal input')
else:
print(f, " = ", result)


Of course, catching the error and just printing illegal input may also hide the real problem to the user, so it may get harder to fix. You may read the section on how to handle exceptions from Python docs for more details.

more