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 with``solve`` 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``.