Lecture 6: Symbols, Variables, and References ============================================= While Sage works with dynamic typing in a similar fashion as Python, sometimes we must :index:declare variables explicitly with var as in var('x'). Every variable in Sage has a type. We distinguish between names of variables and the objects variables refer to. Putting quotes around a variable name prevents an evaluation and we see how this may be used to make connections between variables. We cover the (partial) evaluation of expressions as needed in the verification of the solutions of a general cubic equation. Expressions and Names --------------------- Sage export mathematical constants, such as pi. We can work with pi as a variable and :index:assign any value to pi. For example: :: print(pi, type(pi)) pi = 3.14 print(pi, type(pi)) The first statement shows that pi is an expression. After the assignment, pi refers to the value 3.14 which is of type sage.rings.real_mpfr.RealLiteral. Did we then loose the value of pi? With restore (we could also call the :index:restore the :index:unassign operation) we can get back the original value of pi. :: restore('pi') print(pi, type(pi)) print(numerical_approx(pi, digits=30)) Mind the quotes in the argument of restore, without the quotes we would take the value pi refers to. After executing the restore we see that pi is again an expression. And sure enough, we can see as many digits of pi as we like. The quotes are of course a general construction from Python where everything put between quotes is a string. A value can be stored as a string and then later evaluated with eval. :: x = 3.14 name = 'x' print(x, name, eval(name)) Verification of Solutions ------------------------- By default, with we :index:solve an equation withsolve we receive a list of expressions. :: var('z') equ = z**2 - 3 == 0 sols = solve(equ, z); print(sols) print(type(sols[0])) We have the option to return a list of dictionaries. :: sols = solve(equ, z, solution_dict=True); print(sols) Now we see [{z: -sqrt(3)}, {z: sqrt(3)}] and sols is a list of dictionaries. As key for each :index:dictionary we have the variable name and its corresponding value is the value of the solution of the equation. For example, to select the value of the first solution, using as key the variable name z, we can proceed as follows :: print(sols[0], type(sols[0])) print(sols[0][z]) The last command prints the value -sqrt(3). The dictionary is useful to :index:substitute the value of the variable in the equation we solved, for :index:verification purposes. :: equ.substitute(sols[0]) and we see 0 == 0. Suppose we were to assign to z. Then we can no longer access the dictionary as directly as before, because z now refers to a value, but via keys() we retrieve the unevaluated variable with the corresponding value. But the substitution still works. :: z = 3; print('z = ', z) print(sols[0].keys(), sols[0].values()) equ.substitute(sols[0]) Even though we have lost the use of z as a general variable, its former value as a solution is still contained in the dictionary of solutions sols. How do we see the current value to which z refers to? :: print(eval(str(sols[0].keys()))) This will show the list [3]. Recall that lists in Python allow to work with shared references. Evaluation of Expressions ------------------------- We can express the roots of a polynomial of degree two with symbolic coefficients. Similar formulas exist for a polynomial of degree three. To start over, we clear all the variables in our worksheet with the reset(). :: reset() var('x, a, b, c') p = x^3 + a*x^2 + b*x + c s = solve(p == 0, x, solution_dict=True) print(s) The formulas look complicated. Let us check a specific example. We want to verify the solution for specific values of the coefficients. An easy choice for the coefficients are the numbers 1.0, 2.0, 3.0 (of type float). Recall that Python allows for simultaneous or :index:tuple assignment. :: (a, b, c) = (1.0, 2.0, 3.0) print(a, b, c) print(p) print(s[0]) The outcome is not what we wanted and expected. Even as we see the specific values for a, b, and c printed, the polynomial still shows up in its original symbolic form \ :math:x^3 + a x^2 + b x + c, and so does its solution. If we were to retype the expression for the polynomial again, then the coefficients would be evaluated, but this is tedious and we do not want to retype the complicated expressions for the solutions. How to force the :index:evaluation of the coefficients in p and the solution without retyping the polynomial p? We can evaluate an expression. :: print(p(x=x,a=a,b=b,c=c)) s0 = s[0][x](a=a,b=b,c=c); print(s0) Now we see the polynomial x^3 + x^2 + 2.00000000000000*x + 3.00000000000000 and a numerical value for the solution. We can then evaluate the expression p at s0. :: print(p(x=s0,a=a,b=b,c=c)) To verify whether the value of the expression at the solution will evaluate to zero, we convert to the complex floating point type. :: print(complex(p(x=s0,a=a,b=b,c=c))) and we see that the value is close enough to the machine precision. References and Shared Values ---------------------------- We first :index:reset all variables with reset(). Then we make variables to share the same value. :: reset() var('x, y, z') x = y y = z z = 3 print(x, y, z) What is printed is the sequence y z 3 which means that x refers to y, y refers to z, and z refers to 3. The connections between the variables are shown in :numref:figreferences. .. _figreferences: .. figure:: ./figreferences.png :align: center names of variables as references to other variables or values .. index:: eval() We can track those references with the eval() command. The eval() takes a string as argument. With repeated evals we can get from x to 3. :: ex = eval(str(x)); print x ey = eval(str(ex)); print ey print(eval(str(eval(str(x))))) The first eval shows y, the second one 3, just as the third nested application of eval. Observe that the order of the assignments matters. :: reset() var('x, y, z') z = 3 y = z x = y print(x, y, z) .. index:: prevent evaluation, assignment Because the right hand side in an assignment operation gets evaluated, all variables will receive the same value 3. To prevent the evaluation of the right hand side, we use :index:quotes. :: reset() var('x, y, z') z = 3 y = 'z' x = 'y' print(x, y, z) The sequence that is printed is y, z, 3. Assignments ----------- 1. For variables a and b consider the expression .. math:: {\displaystyle \frac{1}{a + b \sqrt{2}}} We want to compute expressions for the coefficients of the inverse of \ :math:a + b \sqrt{2}, written in the same format, that is: as \ :math:c + d \sqrt{2} for some variables c and d. 1. Set up the equations on the coefficients c and d such that .. math:: {\displaystyle \left( a + b \sqrt{2} \right) \left( c + d \sqrt{2} \right) = 1.} 2. Apply solve to the equations to find expressions for c and d in function of the a and b of the original expression. 3. Verify your solution by making the product \ :math:{\displaystyle \left(a + b \sqrt{2}\right)\left(c + d \sqrt{2}\right)} and check if the product equals 1 for the computed expressions for c and d. 2. Execute the statements reset(); a, b = var('a, b'); b = a; a = 2 and explain the relationships between the variables a and b. Give the Sage commands and their output to illustrate your explanation. 3. Execute the statements reset(); a, b = var('a, b'); a = 3 in a cell. What is the next statement so print(a, b) shows 3, a as b refers to a, as a refers to 3.