Additional Constants¶
The imaginary number $i=\sqrt{-1}$ and infinity $\infty$.
The imaginary number $i = \sqrt{-1}$ is denoted by either i
or I
in Sage. I prefer to use I
because I often use i
for an integer variable.
I
I
I^2
-1
i^2
-1
The “constant” $+\infty$ can be accessed with either Infinity
or oo
.
Infinity
+Infinity
Below we verify that Infinity
is indeed $\infty$ by comparing it with a positive number.
10^100 < Infinity
True
By negating, we get $-\infty$.
-Infinity
-Infinity
Here is another way to write infinity.
oo
+Infinity
Documentation within the notebook¶
By appending a ?
to a function or method, you can view some documentation for it which describes how to use the particular function or method, often with examples. A few examples of this follow.
factorial?
Type: Function_factorial String form: factorial File: ~/Git/sage/sage/src/sage/functions/other.py Docstring: Returns the factorial of n. INPUT: * "n" -- a non-negative integer, a complex number (except negative integers) or any symbolic expression OUTPUT: an integer or symbolic expression EXAMPLES: sage: factorial(0) 1 sage: factorial(4) 24 sage: factorial(10) 3628800 sage: factorial(6) == 6*5*4*3*2 True sage: x = SR.var('x') sage: f = factorial(x + factorial(x)); f factorial(x + factorial(x)) sage: f(x=3) 362880 sage: factorial(x)^2 factorial(x)^2 To prevent automatic evaluation use the "hold" argument: sage: factorial(5, hold=True) factorial(5) To then evaluate again, we currently must use Maxima via "sage.symbolic.expression.Expression.simplify()": sage: factorial(5, hold=True).simplify() 120 We can also give input other than nonnegative integers. For other nonnegative numbers, the "sage.functions.gamma.gamma()" function is used: sage: factorial(1/2) 1/2*sqrt(pi) sage: factorial(3/4) gamma(7/4) sage: factorial(2.3) 2.68343738195577 But negative input always fails: sage: factorial(-32) Traceback (most recent call last): ... ValueError: factorial only defined for non-negative integers And very large integers remain unevaluated: sage: factorial(2**64) factorial(18446744073709551616) sage: SR(2**64).factorial() factorial(18446744073709551616) Init docstring: Returns the factorial of n. INPUT: * "n" -- a non-negative integer, a complex number (except negative integers) or any symbolic expression OUTPUT: an integer or symbolic expression EXAMPLES: sage: factorial(0) 1 sage: factorial(4) 24 sage: factorial(10) 3628800 sage: factorial(6) == 6*5*4*3*2 True sage: x = SR.var('x') sage: f = factorial(x + factorial(x)); f factorial(x + factorial(x)) sage: f(x=3) 362880 sage: factorial(x)^2 factorial(x)^2 To prevent automatic evaluation use the "hold" argument: sage: factorial(5, hold=True) factorial(5) To then evaluate again, we currently must use Maxima via "sage.symbolic.expression.Expression.simplify()": sage: factorial(5, hold=True).simplify() 120 We can also give input other than nonnegative integers. For other nonnegative numbers, the "sage.functions.gamma.gamma()" function is used: sage: factorial(1/2) 1/2*sqrt(pi) sage: factorial(3/4) gamma(7/4) sage: factorial(2.3) 2.68343738195577 But negative input always fails: sage: factorial(-32) Traceback (most recent call last): ... ValueError: factorial only defined for non-negative integers And very large integers remain unevaluated: sage: factorial(2**64) factorial(18446744073709551616) sage: SR(2**64).factorial() factorial(18446744073709551616) Call docstring: Evaluate this function on the given arguments and return the result. EXAMPLES: sage: exp(5) e^5 sage: gamma(15) 87178291200 Python float, Python complex, mpmath mpf and mpc as well as numpy inputs are sent to the relevant "math", "cmath", "mpmath" or "numpy" function: sage: cos(1.r) 0.5403023058681398 sage: assert type(_) is float sage: gamma(4.r) 6.0 sage: assert type(_) is float sage: cos(1jr) # abstol 1e-15 (1.5430806348152437-0j) sage: assert type(_) is complex sage: import mpmath sage: cos(mpmath.mpf('1.321412')) mpf('0.24680737898640387') sage: cos(mpmath.mpc(1,1)) mpc(real='0.83373002513114902', imag='-0.98889770576286506') sage: import numpy sage: sin(numpy.int32(0)) 0.0 sage: type(_) <class 'numpy.float64'>
Apparently, Sage can compute the Gamma function $\Gamma$ (which is a generalization of the factorial) as well.
factorial(3/2)
3/4*sqrt(pi)
factorial(I)
factorial(I)
e?
Type: E String form: e File: ~/Git/sage/sage/src/sage/symbolic/expression.pyx Docstring: Dummy class to represent base of the natural logarithm. The base of the natural logarithm "e" is not a constant in GiNaC/Sage. It is represented by "exp(1)". This class provides a dummy object that behaves well under addition, multiplication, etc. and on exponentiation calls the function "exp". EXAMPLES: The constant defined at the top level is just "exp(1)": sage: e.operator() exp sage: e.operands() [1] Arithmetic works: sage: e + 2 e + 2 sage: 2 + e e + 2 sage: 2*e 2*e sage: e*2 2*e sage: x*e x*e sage: var('a,b') (a, b) sage: t = e^(a+b); t e^(a + b) sage: t.operands() [a + b] Numeric evaluation, conversion to other systems, and pickling works as expected. Note that these are properties of the "exp()" function, not this class: sage: RR(e) 2.71828182845905 sage: R = RealField(200); R Real Field with 200 bits of precision sage: R(e) 2.7182818284590452353602874713526624977572470936999595749670 sage: em = 1 + e^(1-e); em e^(-e + 1) + 1 sage: R(em) 1.1793740787340171819619895873183164984596816017589156131574 sage: maxima(e).float() 2.718281828459045 sage: t = mathematica(e) # optional - mathematica sage: t # optional - mathematica E sage: float(t) # optional - mathematica 2.718281828459045... sage: loads(dumps(e)) e sage: float(e) 2.718281828459045... sage: e.__float__() 2.718281828459045... sage: e._mpfr_(RealField(100)) 2.7182818284590452353602874714 sage: e._real_double_(RDF) # abs tol 5e-16 2.718281828459045 sage: import sympy sage: sympy.E == e # indirect doctest True Init docstring: Dummy class to represent base of the natural logarithm. The base of the natural logarithm "e" is not a constant in GiNaC/Sage. It is represented by "exp(1)". This class provides a dummy object that behaves well under addition, multiplication, etc. and on exponentiation calls the function "exp". EXAMPLES: The constant defined at the top level is just "exp(1)": sage: e.operator() exp sage: e.operands() [1] Arithmetic works: sage: e + 2 e + 2 sage: 2 + e e + 2 sage: 2*e 2*e sage: e*2 2*e sage: x*e x*e sage: var('a,b') (a, b) sage: t = e^(a+b); t e^(a + b) sage: t.operands() [a + b] Numeric evaluation, conversion to other systems, and pickling works as expected. Note that these are properties of the "exp()" function, not this class: sage: RR(e) 2.71828182845905 sage: R = RealField(200); R Real Field with 200 bits of precision sage: R(e) 2.7182818284590452353602874713526624977572470936999595749670 sage: em = 1 + e^(1-e); em e^(-e + 1) + 1 sage: R(em) 1.1793740787340171819619895873183164984596816017589156131574 sage: maxima(e).float() 2.718281828459045 sage: t = mathematica(e) # optional - mathematica sage: t # optional - mathematica E sage: float(t) # optional - mathematica 2.718281828459045... sage: loads(dumps(e)) e sage: float(e) 2.718281828459045... sage: e.__float__() 2.718281828459045... sage: e._mpfr_(RealField(100)) 2.7182818284590452353602874714 sage: e._real_double_(RDF) # abs tol 5e-16 2.718281828459045 sage: import sympy sage: sympy.E == e # indirect doctest True Call docstring: Call the "subs()" on this expression. EXAMPLES: sage: var('x,y,z') (x, y, z) sage: (x+y)(x=z^2, y=x^y) z^2 + x^y
Below we defined a variable fact_or_fiction
and assigned the value $34$ to it. Then calling fact_or_fiction?
gives documentation for the Integer
class.
fact_or_fiction = 34
fact_or_fiction?
Type: Integer String form: 34 File: ~/Git/sage/sage/src/sage/rings/integer.pyx Docstring: The "Integer" class represents arbitrary precision integers. It derives from the "Element" class, so integers can be used as ring elements anywhere in Sage. The constructor of "Integer" interprets strings that begin with "0o" as octal numbers, strings that begin with "0x" as hexadecimal numbers and strings that begin with "0b" as binary numbers. The class "Integer" is implemented in Cython, as a wrapper of the GMP "mpz_t" integer type. EXAMPLES: sage: Integer(123) 123 sage: Integer("123") 123 Sage Integers support **PEP 3127** literals: sage: Integer('0x12') 18 sage: Integer('-0o12') -10 sage: Integer('+0b101010') 42 Conversion from PARI: sage: Integer(pari('-10380104371593008048799446356441519384')) -10380104371593008048799446356441519384 sage: Integer(pari('Pol([-3])')) -3 Conversion from gmpy2: sage: from gmpy2 import mpz sage: Integer(mpz(3)) 3 Init docstring: EXAMPLES: sage: a = int(-901824309821093821093812093810928309183091832091) sage: b = ZZ(a); b -901824309821093821093812093810928309183091832091 sage: ZZ(b) -901824309821093821093812093810928309183091832091 sage: ZZ('-901824309821093821093812093810928309183091832091') -901824309821093821093812093810928309183091832091 sage: ZZ(int(-93820984323)) -93820984323 sage: ZZ(ZZ(-901824309821093821093812093810928309183091832091)) -901824309821093821093812093810928309183091832091 sage: ZZ(QQ(-901824309821093821093812093810928309183091832091)) -901824309821093821093812093810928309183091832091 sage: ZZ(RR(2.0)^80) 1208925819614629174706176 sage: ZZ(QQbar(sqrt(28-10*sqrt(3)) + sqrt(3))) 5 sage: ZZ(AA(32).nth_root(5)) 2 sage: ZZ(pari('Mod(-3,7)')) 4 sage: ZZ('sage') Traceback (most recent call last): ... TypeError: unable to convert 'sage' to an integer sage: Integer('zz',36).str(36) 'zz' sage: ZZ('0x3b').str(16) '3b' sage: ZZ( ZZ(5).digits(3) , 3) 5 sage: import numpy sage: ZZ(numpy.int64(7^7)) 823543 sage: ZZ(numpy.ubyte(-7)) 249 sage: ZZ(True) 1 sage: ZZ(False) 0 sage: ZZ(1==0) 0 sage: ZZ('+10') 10 sage: from gmpy2 import mpz sage: ZZ(mpz(42)) 42 sage: k = GF(2) sage: ZZ((k(0),k(1)), 2) 2 sage: ZZ(float(2.0)) 2 sage: ZZ(float(1.0/0.0)) Traceback (most recent call last): ... OverflowError: cannot convert float infinity to integer sage: ZZ(float(0.0/0.0)) Traceback (most recent call last): ... ValueError: cannot convert float NaN to integer sage: class MyInt(int): ....: pass sage: class MyFloat(float): ....: pass sage: ZZ(MyInt(3)) 3 sage: ZZ(MyFloat(5)) 5 sage: Integer('0') 0 sage: Integer('0X2AEEF') 175855 Test conversion from PARI (https://github.com/sagemath/sage/issues/11685): sage: ZZ(pari(-3)) -3 sage: ZZ(pari("-3.0")) -3 sage: ZZ(pari("-3.5")) Traceback (most recent call last): ... TypeError: Attempt to coerce non-integral real number to an Integer sage: ZZ(pari("1e100")) Traceback (most recent call last): ... PariError: precision too low in truncr (precision loss in truncation) sage: ZZ(pari("10^50")) 100000000000000000000000000000000000000000000000000 sage: ZZ(pari("Pol(3)")) 3 sage: ZZ(GF(3^20,'t')(1)) 1 sage: ZZ(pari(GF(3^20,'t')(1))) 1 sage: x = polygen(QQ) sage: K.<a> = NumberField(x^2 + 3) sage: ZZ(a^2) -3 sage: ZZ(pari(a)^2) -3 sage: ZZ(pari("Mod(x, x^3+x+1)")) # Note error message refers to lifted element Traceback (most recent call last): ... TypeError: Unable to coerce PARI x to an Integer Test coercion of p-adic with negative valuation: sage: ZZ(pari(Qp(11)(11^-7))) Traceback (most recent call last): ... TypeError: cannot convert p-adic with negative valuation to an integer Test converting a list with a very large base: sage: a = ZZ(randint(0, 2^128 - 1)) sage: L = a.digits(2^64) sage: a == sum([x * 2^(64*i) for i,x in enumerate(L)]) True sage: a == ZZ(L, base=2^64) True Test comparisons with numpy types (see https://github.com/sagemath/sage/issues/13386 and https://github.com/sagemath/sage/issues/18076): sage: import numpy sage: numpy.int8('12') == 12 True sage: 12 == numpy.int8('12') True sage: float('15') == 15 True sage: 15 == float('15') True Test underscores as digit separators (PEP 515, https://www.python.org/dev/peps/pep-0515/): sage: Integer('1_3') 13 sage: Integer(b'1_3') 13
Domains in variables¶
For more information on var
see the documentation or type var?
. Note that you can specify a domain of a symbolic variable.
The domain of a variable is the set containing it. By default, the domain of a symbolic variable is the complex numbers, $\mathbb C$.
var?
Docstring: Create a symbolic variable with the name *s*. INPUT: * "args" -- A single string "var('x y')", a list of strings "var(['x','y'])", or multiple strings "var('x', 'y')". A single string can be either a single variable name, or a space or comma separated list of variable names. In a list or tuple of strings, each entry is one variable. If multiple arguments are specified, each argument is taken to be one variable. Spaces before or after variable names are ignored. * "kwds" -- keyword arguments can be given to specify domain and custom latex_name for variables. See EXAMPLES for usage. Note: The new variable is both returned and automatically injected into the global namespace. If you need a symbolic variable in library code, you must use either "SR.var()" or "SR.symbol()". OUTPUT: If a single symbolic variable was created, the variable itself. Otherwise, a tuple of symbolic variables. The variable names are checked to be valid Python identifiers and a "ValueError" is raised otherwise. EXAMPLES: Here are the different ways to define three variables "x", "y", and "z" in a single line: sage: var('x y z') (x, y, z) sage: var('x, y, z') (x, y, z) sage: var(['x', 'y', 'z']) (x, y, z) sage: var('x', 'y', 'z') (x, y, z) sage: var('x'), var('y'), var(z) (x, y, z) We define some symbolic variables: sage: var('n xx yy zz') (n, xx, yy, zz) Then we make an algebraic expression out of them: sage: f = xx^n + yy^n + zz^n; f xx^n + yy^n + zz^n By default, var returns a complex variable. To define real or positive variables we can specify the domain as: sage: x = var('x', domain=RR); x; x.conjugate() x x sage: y = var('y', domain='real'); y.conjugate() y sage: y = var('y', domain='positive'); y.abs() y Custom latex expression can be assigned to variable: sage: x = var('sui', latex_name="s_{u,i}"); x._latex_() '{s_{u,i}}' In notebook, we can also colorize latex expression: sage: x = var('sui', latex_name="\\color{red}{s_{u,i}}"); x._latex_() '{\\color{red}{s_{u,i}}}' We can substitute a new variable name for n: sage: f(n = var('sigma')) xx^sigma + yy^sigma + zz^sigma If you make an important built-in variable into a symbolic variable, you can get back the original value using restore: sage: var('QQ RR') (QQ, RR) sage: QQ QQ sage: restore('QQ') sage: QQ Rational Field We make two new variables separated by commas: sage: var('theta, gamma') (theta, gamma) sage: theta^2 + gamma^3 gamma^3 + theta^2 The new variables are of type Expression, and belong to the symbolic expression ring: sage: type(theta) <class 'sage.symbolic.expression.Expression'> sage: parent(theta) Symbolic Ring Init docstring: Initialize self. See help(type(self)) for accurate signature. File: ~/Git/sage/sage/src/sage/calculus/var.pyx Type: builtin_function_or_method
var('z')
z
This example shows that $z$ is complex. If $z$ was real, we'd get $z^2$ here:
sqrt(z^4)
sqrt(z^4)
var('x', domain='real')
x
Since $x$ is real, the following expression is simplified.
sqrt(x^4)
x^2
You can also specify that a variable is a positive real.
var('y', domain='positive')
y
This enables Sage to simplify $\sqrt{y}$.
sqrt(y^2)
y
It also can correctly evaluate the inequality $y > 0$:
bool(y > 0)
True
Since $x$ is real, the inequality $x>0$ may be true or false. Since SageMath cannot prove that $x>0$ is True, it returns False
:
bool(x>0)
False
Expressions¶
Pretty printing¶
Sage allows for ways to illustrate math in a pretty format using show
.
var('theta')
theta
show(theta)
z = cos(2*theta) + sin(theta) + exp(theta)
z
cos(2*theta) + e^theta + sin(theta)
show(z)
Latex formatting of expressions¶
We can also get the LaTeX code for things and use it in a TeX editor to typeset math. We use the latex
function:
latex(z)
\cos\left(2 \, \theta\right) + e^{\theta} + \sin\left(\theta\right)
Simple plotting¶
We can plot a variety of things in Sage. Below we plot some functions that we defined above.
From above, z
is a function of $\theta$:
z
cos(2*theta) + e^theta + sin(theta)
The following will plot $z$ as a function of $\theta$ between $-1$ and $2$.
plot(z, (theta, -1, 2))
w = sin(theta)
We can specify colors for plots as follows.
plot(w, (theta, -1, 2), color='red')
Multiple plots can be drawn together as below. We use +
to add plots to display them on the same axes:
plt1 = plot(z, (theta, -1, 2))
plt2 = plot(w, (theta, -1, 2), color='red')
plt1 + plt2
Substitution¶
Sage can do some substitution as follows.
var('x y')
(x, y)
z = x^2 * y + 3*y
z
x^2*y + 3*y
Setting $x=3$ in $z$ as above gives $12y$, as it should!
z(x = 3)
12*y
Setting $y=2$.
z(y=2)
2*x^2 + 6
Can substitute multiple variables together also.
z(x=7, y=1)
52
var('t')
t
Can do symbolic substitution also.
z(x=t^2-17)
(t^2 - 17)^2*y + 3*y
z
x^2*y + 3*y
Using the subs
method. It's a bit mysterious, but if it sees an expression just as it is written, it can do the substitution.
z.subs(3*y == t)
x^2*y + t
z.subs(x*y == t)
x^2*y + 3*y
Changing forms¶
var('x y')
(x, y)
z = x*(y+1) - 7*x
show(z)
z.simplify()
x*(y + 1) - 7*x
z.simplify?
Docstring: Return a simplified version of this symbolic expression. INPUT: * "algorithm" -- one of : * "maxima" : (default) sends the expression to "maxima" and converts it back to Sage * "sympy" : converts the expression to "sympy", simplifies it (passing any optional keyword(s)), and converts the result to Sage * "giac" : converts the expression to "giac", simplifies it, and converts the result to Sage * "fricas" : converts the expression to "fricas", simplifies it, and converts the result to Sage See also: "simplify_full()", "simplify_trig()", "simplify_rational()", "simplify_rectform()" "simplify_factorial()", "simplify_log()", "simplify_real()", "simplify_hypergeometric()", "canonicalize_radical()" EXAMPLES: sage: a = var('a'); f = x*sin(2)/(x^a); f x*sin(2)/x^a sage: f.simplify() x^(-a + 1)*sin(2) Some simplifications are quite algorithm-specific: sage: x, t = var("x, t") sage: ex = cos(t).exponentialize() sage: ex = ex.subs((sin(t).exponentialize()==x).solve(t)[0]) sage: ex 1/2*I*x + 1/2*I*sqrt(x^2 - 1) + 1/2/(I*x + I*sqrt(x^2 - 1)) sage: ex.simplify() 1/2*I*x + 1/2*I*sqrt(x^2 - 1) + 1/(2*I*x + 2*I*sqrt(x^2 - 1)) sage: ex.simplify(algorithm="sympy") I*(x^2 + sqrt(x^2 - 1)*x - 1)/(x + sqrt(x^2 - 1)) sage: ex.simplify(algorithm="giac") I*sqrt(x^2 - 1) sage: ex.simplify(algorithm="fricas") (I*x^2 + I*sqrt(x^2 - 1)*x - I)/(x + sqrt(x^2 - 1)) Init docstring: Initialize self. See help(type(self)) for accurate signature. File: ~/Git/sage/sage/src/sage/symbolic/expression.pyx Type: builtin_function_or_method
z.simplify(algorithm='maxima')
x*(y + 1) - 7*x
z.simplify(algorithm='sympy')
x*(y - 6)
z
x*(y + 1) - 7*x
zz = z.simplify(algorithm='sympy')
zz
x*(y - 6)
z
x*(y + 1) - 7*x
The .factor()
method does what you think it does.
z.factor()
x*(y - 6)
It also applies to integers:
100.factor()
2^2 * 5^2
Here we consider an example with an obvious simplification:
z = (x^2 + x) / x
z
(x^2 + x)/x
Note that $x$ wasn't automatically canceled. Neither is it when we do a .simplify()
:
z.simplify()
(x^2 + x)/x
But there is a .simplify_rational()
which simplifies a rational expression.
z.simplify_rational()
x + 1
The .full_simplify()
method does a sequence of simplifications including .simplify_rational()
. In general this is something you want to apply if you hope to get the simplest form you can possibly get (but it doesn't always work...).
zz = z.full_simplify()
zz
x + 1
Consider the following equation. Remember we can force evaluation by enclosing in bool
.
z == zz
(x^2 + x)/x == x + 1
It's worth pointing out that $z$ is the same as $zz$ only when $x \neq 0$. So, it is not clear (to me) what criterion they are using to say that these two expressions are the “same.”
bool(z == zz)
True
Sage admits that it cannot evaluate $z$ when $x=0$. But it can obviously evaluate $zz$ when $x=0$. Just a caveat.
z(x=0)
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[62], line 1 ----> 1 z(x=Integer(0)) File ~/Git/sage/sage/src/sage/symbolic/expression.pyx:6186, in sage.symbolic.expression.Expression.__call__() 6184 z^2 + x^y 6185 """ -> 6186 return self._parent._call_element_(self, *args, **kwds) 6187 6188 def variables(self): File ~/Git/sage/sage/src/sage/symbolic/ring.pyx:1045, in sage.symbolic.ring.SymbolicRing._call_element_() 1043 "can use named arguments instead, like " 1044 "EXPR(x=..., y=...)") -> 1045 return _the_element.subs(d, **kwds) 1046 1047 def subring(self, *args, **kwds): File ~/Git/sage/sage/src/sage/symbolic/expression.pyx:5898, in sage.symbolic.expression.Expression.subs() 5896 smap.insert(make_pair((<Expression>self.coerce_in(k))._gobj, 5897 (<Expression>self.coerce_in(v))._gobj)) -> 5898 res = self._gobj.subs_map(smap, 0) 5899 return new_Expression_from_GEx(self._parent, res) 5900 ValueError: power::eval(): division by zero
Example "Proof"¶
Theorem Let $a$, $b$, and $c$ be points on a circle and let $d$ be the center of the circle. Then $\angle bdc = 2 \angle bac$.
We work in $\mathbb C$. Up to a Euclidean similarity, we may assume that $d=0$ and $a=1$. Then $$b = \cos \beta+i \sin \beta \quad \text{and} \quad c=\cos \gamma + i \sin \gamma$$ for some angles $\beta$ and $\gamma$.
var('beta gamma', domain='real')
(beta, gamma)
a = 1
d = 0
b = cos(beta)+I*sin(beta)
c = cos(gamma) + I*sin(gamma)
Basic fact: A complex number $z$ has an argument (angle with positive real axis) and a modulus (distance from zero). Multiplication by $z$ rotates the plane by $\arg z$ about the origin and scales the plane by the modulus $|z|$.
Thus, multiplication by $c/b$ rotates by $\angle bdc$. (This multiplication fixes $d=0$ and sends $b$ to $c$.) Similarly multiplication by $\frac{c-a}{b-a}$ rotates by $\angle bac$. (This multiplication sends $b-a$ to $c-a$. Note that subtraction by $a$ translates the plane.) It follows that multiplication by $$z = \left(\frac{c-a}{b-a}\right)^2 \Big/ \frac{c}{b}$$ rotates the plane by $2 \angle bac - \angle bdc$ (and scales by an irrelevant amount). Thus to show $2 \angle bac - \angle bdc=0$, we can show that $\arg z=0$ or equivalently that $z$ is a positive real number.
Here is a our complex number $z$ that has the property that multiplication by it rotates by $2 \angle bac - \angle bdc$:
z = ((c-a)/(b-a))^2 / (c/b)
z
(cos(beta) + I*sin(beta))*(cos(gamma) + I*sin(gamma) - 1)^2/((cos(beta) + I*sin(beta) - 1)^2*(cos(gamma) + I*sin(gamma)))
We remark that any complex number can be written in the form $z = z_1 + i z_2$ where $z_1, z_2 \in \mathbb R$. The number $z_1$ is the real part, and the number $z_2$ is the imaginary part. These parts can be accessed with z.real()
and z.imag()
.
Below, we check to see that the imaginary part of $z$ is $0$, so it is indeed a real number:
bool(z.imag()==0)
True
Another way to check this would be as follows:
bool(z == z.real())
True
Anyway, this is great. We already know $z$ is real. But, we still need to show it is positive.
We will isolate the real part of it, and use standard simplification methods.
zr = z.real()
zr
cos(beta)^3*cos(gamma)^3/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(beta)*cos(gamma)^3*sin(beta)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(beta)^2*cos(gamma)^2*sin(beta)*sin(gamma)/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(gamma)^2*sin(beta)^3*sin(gamma)/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(beta)^3*cos(gamma)*sin(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(beta)*cos(gamma)*sin(beta)^2*sin(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(beta)^2*sin(beta)*sin(gamma)^3/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + sin(beta)^3*sin(gamma)^3/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(beta)^3*cos(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(beta)^2*cos(gamma)^3/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(beta)*cos(gamma)^2*sin(beta)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(gamma)^3*sin(beta)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(beta)^3*sin(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(beta)^2*cos(gamma)*sin(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(beta)*sin(beta)^2*sin(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(gamma)*sin(beta)^2*sin(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(beta)^3*cos(gamma)/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + 4*cos(beta)^2*cos(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(beta)*cos(gamma)^3/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(beta)*cos(gamma)*sin(beta)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + 4*cos(gamma)^2*sin(beta)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - cos(beta)^2*sin(beta)*sin(gamma)/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - cos(gamma)^2*sin(beta)*sin(gamma)/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - sin(beta)^3*sin(gamma)/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + 4*cos(beta)^2*sin(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(beta)*cos(gamma)*sin(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + 4*sin(beta)^2*sin(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - sin(beta)*sin(gamma)^3/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(beta)^2*cos(gamma)/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(beta)*cos(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(gamma)*sin(beta)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) - 2*cos(beta)*sin(gamma)^2/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + cos(beta)*cos(gamma)/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2)) + sin(beta)*sin(gamma)/((cos(beta)^4 + 2*cos(beta)^2*sin(beta)^2 + sin(beta)^4 - 4*cos(beta)^3 - 4*cos(beta)*sin(beta)^2 + 6*cos(beta)^2 + 2*sin(beta)^2 - 4*cos(beta) + 1)*(cos(gamma)^2 + sin(gamma)^2))
We will just experiment. First we try trig_reduce
:
zr2 = zr.trig_reduce()
zr2
-1/64*(cos(3*beta + 3*gamma) + 3*cos(3*beta + gamma) - cos(beta + 3*gamma) - 3*cos(beta + gamma) - cos(-beta + 3*gamma) - 3*cos(-beta + gamma) + cos(-3*beta + 3*gamma) + 3*cos(-3*beta + gamma))/(cos(2*beta) - 4*cos(beta) + 3) - 1/64*(cos(3*beta + 3*gamma) + cos(3*beta + gamma) + cos(beta + 3*gamma) + cos(beta + gamma) - cos(-beta + 3*gamma) - cos(-beta + gamma) - cos(-3*beta + 3*gamma) - cos(-3*beta + gamma))/(cos(2*beta) - 4*cos(beta) + 3) + 1/64*(cos(3*beta + 3*gamma) + cos(3*beta + gamma) - 3*cos(beta + 3*gamma) - 3*cos(beta + gamma) + 3*cos(-beta + 3*gamma) + 3*cos(-beta + gamma) - cos(-3*beta + 3*gamma) - cos(-3*beta + gamma))/(cos(2*beta) - 4*cos(beta) + 3) - 1/64*(cos(3*beta + 3*gamma) - cos(3*beta + gamma) + 3*cos(beta + 3*gamma) - 3*cos(beta + gamma) + 3*cos(-beta + 3*gamma) - 3*cos(-beta + gamma) + cos(-3*beta + 3*gamma) - cos(-3*beta + gamma))/(cos(2*beta) - 4*cos(beta) + 3) + 1/64*(cos(3*beta + 3*gamma) - cos(3*beta + gamma) - cos(beta + 3*gamma) + cos(beta + gamma) - cos(-beta + 3*gamma) + cos(-beta + gamma) + cos(-3*beta + 3*gamma) - cos(-3*beta + gamma))/(cos(2*beta) - 4*cos(beta) + 3) + 1/64*(cos(3*beta + 3*gamma) - 3*cos(3*beta + gamma) + cos(beta + 3*gamma) - 3*cos(beta + gamma) - cos(-beta + 3*gamma) + 3*cos(-beta + gamma) - cos(-3*beta + 3*gamma) + 3*cos(-3*beta + gamma))/(cos(2*beta) - 4*cos(beta) + 3) + 1/16*(cos(3*beta + gamma) + cos(beta + gamma) - cos(-beta + gamma) - cos(-3*beta + gamma))/(cos(2*beta) - 4*cos(beta) + 3) - 1/16*(cos(3*beta + gamma) - cos(beta + gamma) - cos(-beta + gamma) + cos(-3*beta + gamma))/(cos(2*beta) - 4*cos(beta) + 3) + 1/16*(cos(beta + 3*gamma) + cos(beta + gamma) - cos(-beta + 3*gamma) - cos(-beta + gamma))/(cos(2*beta) - 4*cos(beta) + 3) - 1/16*(cos(beta + 3*gamma) - cos(beta + gamma) + cos(-beta + 3*gamma) - cos(-beta + gamma))/(cos(2*beta) - 4*cos(beta) + 3) + 3/32*cos(3*beta + gamma)/(cos(2*beta) - 4*cos(beta) + 3) + 3/32*cos(beta + 3*gamma)/(cos(2*beta) - 4*cos(beta) + 3) + 3/4*cos(beta + gamma)/(cos(2*beta) - 4*cos(beta) + 3) - 2*cos(beta)/(cos(2*beta) - 4*cos(beta) + 3) + 1/8*cos(-beta + 3*gamma)/(cos(2*beta) - 4*cos(beta) + 3) + 25/32*cos(-beta + gamma)/(cos(2*beta) - 4*cos(beta) + 3) + 1/32*cos(-3*beta + 3*gamma)/(cos(2*beta) - 4*cos(beta) + 3) + 1/8*cos(-3*beta + gamma)/(cos(2*beta) - 4*cos(beta) + 3) - 2*cos(gamma)/(cos(2*beta) - 4*cos(beta) + 3) + 2/(cos(2*beta) - 4*cos(beta) + 3)
Now since we see a lot of fractions, let's attempt to simplify them:
zr3 = zr2.simplify_rational()
zr3
(cos(beta + gamma) - 2*cos(beta) + cos(-beta + gamma) - 2*cos(gamma) + 2)/(cos(2*beta) - 4*cos(beta) + 3)
This is looking better, but we can't do much algebraic with the expressions in the cosines. So, we expand them:
zr4 = zr3.trig_expand()
zr4
2*(cos(beta)*cos(gamma) - cos(beta) - cos(gamma) + 1)/(cos(beta)^2 - sin(beta)^2 - 4*cos(beta) + 3)
Now the denominator looks a bit weird. The only sine term appears in the denominator, and it is $\sin(\beta)^2$, so it seems like it would be better to convert it into cosine. We'll do this explicitly. Note that we are not changing the value of the expression:
zr5 = zr4.subs(sin(beta)^2 == 1 - cos(beta)^2)
zr5
(cos(beta)*cos(gamma) - cos(beta) - cos(gamma) + 1)/(cos(beta)^2 - 2*cos(beta) + 1)
Now the denomiantor clearly factors. The numerator does too and things cancel:
zr6 = zr5.factor()
zr6
(cos(gamma) - 1)/(cos(beta) - 1)
Now you can see that $z>0$. Indeed $\cos(\gamma)-1 \leq 0$ and $\cos (\beta)-1 \leq 0$, so their ratio is non-negative. In fact, it is possible to see that neither $\cos(\gamma)-1$ nor $\cos(\beta) - 1$ can be zero, because if they were than some of our points ($a$, $b$, or $c$) would have to coincide. So, we conclude that $z>0$. Finishing the proof.
Because the imaginary part of $z$ is zero and the real part is $\frac{\cos(\gamma)-1}{\cos(\beta)-1}$, we actually have $$z = \frac{\cos(\gamma)-1}{\cos(\beta)-1}.$$ We can check this:
bool(z == (cos(gamma) - 1)/(cos(beta) - 1))
True
Note that Sage has a pretty easy time proving equality (if you get it right), but has difficulty with inequalities:
bool(z >= 0)
False
With the simplified version, it is okay:
bool(zr6 >= 0)
True
Sage fuctions and calculus¶
In addition to expressions, you can also define Sage functions, which allow you to use traditional function notation. Below we give an example of a Sage (or symbolic) function:
f(x) = exp(sin(x))
The difference above is the left side is f(x)
rather than just f
as above. Note that this defines two variables: f
and x
.
When we print f
now we can see it is a function:
f
x |--> e^sin(x)
show(f)
We can use traditional function notation for evaluation of the function:
f(7)
e^sin(7)
We can also use the subs
method as before:
f.subs(sin(x) == 1/2)
x |--> e^(1/2)
If we don't want the symbolic function when we do this, we can apply the substitution to f(x)
rather than to f
:
f(x).subs(sin(x) == 1/2)
e^(1/2)
We can also take derivatives:
f
x |--> e^sin(x)
f.derivative()
x |--> cos(x)*e^sin(x)
It is also okay to specify the variable we take a derivative with respect to:
f.derivative(x)
x |--> cos(x)*e^sin(x)
Another example: We will observe how defining a function creates variables. First note that the variable s
is not defined:
s
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[86], line 1 ----> 1 s NameError: name 's' is not defined
The following function definition will define three variables: u
, s
and t
. The variables s
and t
will be symbolic variables.
u(s, t) = 2*s*t^3
s
s
Let's do it again and demonstrate that the current value of t
is overriden. First we define t
to be 7
:
t = 7
t
7
Now we define u
.
u(s, t) = 2*s*t^3
Now observe that t
is no longer $7$. Instead it is a symbolic variable:
t
t
Let's do some more calculus. Some examples of doing differentiation and integration below.
Derivative with respect to $s$:
u.derivative(s)
(s, t) |--> 2*t^3
Derivative with respect to $t$:
u.derivative(t)
(s, t) |--> 6*s*t^2
Total derivative (or gradient):
u.derivative()
(s, t) |--> (2*t^3, 6*s*t^2)
The following calculates the second derivative. Since the first derivative is a function from 2 variables to 2 variables, the second derivative is a $2 \times 2$ matrix.
u.derivative().derivative()
[ (s, t) |--> 0 (s, t) |--> 6*t^2] [ (s, t) |--> 6*t^2 (s, t) |--> 12*s*t]
Another simple example: We define $f(x) = \sin(x)$.
f(x)=sin(x)
Here we compute the definite integral with respect to $x$ from $0$ to $pi$:
f.integrate(x, 0, pi)
2
We can also do indefinite integration with respect to $x$:
f.integrate(x)
x |--> -cos(x)
Now let's go back to our example with two variables.
u(s, t) = 2*s*t^3
Integration with respect to $s$ from $0$ to $1$ integrates away the s
variable, but the t
variable remains:
u.integrate(s, 0, 1)
t |--> t^3
Here we compute the double integral over the rectangle $[0,1] \times [2,4]$ in the $st$-plane.
u.integrate(s, 0, 1).integrate(t, 2, 4)
60
Symbolic solving¶
Problem: Find the points in the plane whose distance from $(3, 5)$ is $7$ and whose distance from $(2, 2)$ is $5$.
var('x y', domain='real')
(x, y)
This defines a circle in the plane with center at $(3,5)$ and radius $7$:
eq1 = (x-3)^2 + (y-5)^2 == 7^2
eq1
(x - 3)^2 + (y - 5)^2 == 49
This defines a circle in the plane with center at $(2,2)$ and radius $5$:
eq2 = (x-2)^2 + (y-2)^2 == 5^2
eq2
(x - 2)^2 + (y - 2)^2 == 25
We use solve
to simultaneously solve the equations of the two circles. It gives the solutions as a list of lists, where each element of the inner list is a point written as a pair of coordinates.
sol = solve([eq1, eq2], [x, y])
sol
[[x == -3/10*sqrt(201) + 13/10, y == 1/10*sqrt(201) - 1/10], [x == 3/10*sqrt(201) + 13/10, y == -1/10*sqrt(201) - 1/10]]
Accessing the first point.
sol[0]
[x == -3/10*sqrt(201) + 13/10, y == 1/10*sqrt(201) - 1/10]
Accessing the $x$-coordinate of the first point.
sol[0][0]
x == -3/10*sqrt(201) + 13/10
Accessing the numerical value of the $x$-coordinate of the first point.
sol[0][0].right_hand_side()
-3/10*sqrt(201) + 13/10
In case you forget the right_hand_side()
method, you can use tab completion. But you have to define an object equal to sol[0][0]
before you can do this:
v = sol[0][0]
Now v.ri<tab>
produces a list. We notice from this that it is also possible to call v.right()
. This seems to be the same as v.right_hand_side()
:
v.right()
-3/10*sqrt(201) + 13/10
I find it easier to work with dictionaries than symbolic equations as above. There is a solution_dict
keyword argument you can use to get solutions returns the points as a dictionary (a map):
sol2 = solve([eq1, eq2], [x, y], solution_dict=True)
sol2
[{x: -3/10*sqrt(201) + 13/10, y: 1/10*sqrt(201) - 1/10}, {x: 3/10*sqrt(201) + 13/10, y: -1/10*sqrt(201) - 1/10}]
The curly brackets above indicate a dictionary. The value sol2
is a list consisting of two dictionaries, the first is:
sol[0] = {x: -3/10*sqrt(201) + 13/10, y: 1/10*sqrt(201) - 1/10}
To access the coordinates, we use square brackets to apply the map in the dictionary. So, to access the coordinates we can use sol[0][x]
and sol[0][y]
:
sol2[0][x]
-3/10*sqrt(201) + 13/10
sol2[0][y]
1/10*sqrt(201) - 1/10