0% found this document useful (0 votes)
10 views160 pages

Computational Mathematics With SageMath 1

The document is a comprehensive guide to using SageMath, a computer algebra system, aimed at students and practitioners in mathematics. It emphasizes a practical approach to symbolic computation while explaining underlying mathematical concepts and algorithms. The book includes contributions from multiple authors and is structured to cater to both undergraduate and graduate studies.

Uploaded by

ftrip2
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views160 pages

Computational Mathematics With SageMath 1

The document is a comprehensive guide to using SageMath, a computer algebra system, aimed at students and practitioners in mathematics. It emphasizes a practical approach to symbolic computation while explaining underlying mathematical concepts and algorithms. The book includes contributions from multiple authors and is structured to cater to both undergraduate and graduate studies.

Uploaded by

ftrip2
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 160

Computational

Mathematics
with SageMath
P. Zimmermann
A. Casamayou
N. Cohen
G. Connan
T. Dumont
L. Fousse
F. Maltey
M. Meulien
M. Mezzarobba
C. Pernet
N. M. Thiéry
E. Bray
J. Cremona
M. Forets
A. Ghitza
H. Thomas
Computational Mathematics with SageMath

Paul Zimmermann Alexandre Casamayou


Nathann Cohen Guillaume Connan Thierry Dumont
Laurent Fousse François Maltey Matthias Meulien
Marc Mezzarobba Clément Pernet Nicolas M. Thiéry
Erik Bray John Cremona Marcelo Forets
Alexandru Ghitza Hugh Thomas
C
This work is distributed under the terms of the license Creative Commons
Attribution-ShareAlike 4.0 International (cc by-sa 4.0).
Extract from https://creativecommons.org/licenses/by-sa/4.0/deed.en:
This is a human-readable summary of (and not a substitute for) the license.
You are free to:
• Share — copy and redistribute the material in any medium or format
• Adapt — remix, transform, and build upon the material for any purpose,
even commercially.
• The licensor cannot revoke these freedoms as long as you follow the license
terms.
Under the following terms:

b Attribution — You must give appropriate credit, provide a link to the


license, and indicate if changes were made. You may do so in any reasonable
manner, but not in any way that suggests the licensor endorses you or your
use.
a ShareAlike — If you remix, transform, or build upon the material, you
must distribute your contributions under the same license as the original.
• No additional restrictions — You may not apply legal terms or techno-
logical measures that legally restrict others from doing anything the license
permits.

Parts of this book are inherited from the book Calcul formel : mode d’emploi.
Exemples en Maple from Philippe Dumas, Claude Gomez, Bruno Salvy and Paul
Zimmermann, distributed under cc-by-sa 2.0 fr, in particular Sections 2.1.5, 2.3.5
and 5.3.
Parts of the Sage examples from Chapter 15 are inherited from the tutorials
of MuPAD-Combinat [HT04] and Sage-combinat. The enumeration of complete
binary trees in §15.1.2 is partly inspired from a classroom problem designed by
Florent Hivert.
Exercise 9 on Gauss problem is inspired from a problem designed by François
Pantigny, and Exercise 17 on the Magnus effect is extracted from a classroom
problem designed by Jean-Guy Stoliaroff.
Graphs from Figure 4.9 and their interpretation reproduce part of paragraph
III.4 from the “Que sais-je ?” book Les nombres premiers from Gérald Tenenbaum
and Michel Mendès France.
Preface

This book was written for those who want to efficiently use a computer algebra
system, and Sage in particular. Symbolic computation systems offer plenty of
functionality, and finding the right approach or command to solve a given problem
is sometimes difficult. A reference manual provides a detailed analytic description
of each function of the system; however, this is not very useful since usually we
do not know in advance the name of the function we are looking for! This book
provides another approach, by giving a global and synthetic point of view, while
insisting on the underlying mathematics, the classes of problems we can solve
and the corresponding algorithms.
The first part, more specific to Sage, will help getting to grips with this
system. This part is written to be understood by undergraduate students, and
partly by high school students. The other parts cover more specialised topics
encountered in undergraduate and graduate studies. Unlike in a reference manual,
the mathematical concepts are clearly explained before illustrating them with
Sage. This book is thus in the first place a book about mathematics.
To illustrate this book, Sage was a natural choice, since it is an open-source
system, that anybody can use, modify and redistribute at will. In particular
the student who learns Sage in high school will be able to continue to use it at
undergraduate or graduate levels, in a company, etc. Sage is still a relatively
young system, and despite its already extensive capacities, it does contain some
bugs. However, thanks to its very active community of developers, Sage evolves
very quickly. Every Sage user can report a bug — maybe together with its solution
— on trac.sagemath.org or via the sage-support list.
In writing this book, we have used version 8.2 of Sage. Nevertheless, the
examples should still work with later versions. However, some of the explanations
may no longer hold, for example the fact that Sage relies on Maxima for numerical
integrals.
When in December 2009 I asked Alexandre Casamayou, Guillaume Connan,
Thierry Dumont, Laurent Fousse, François Maltey, Matthias Meulien, Marc
Mezzarobba, Clément Pernet and Nicolas Thiéry to write the first version (in
French) of this book, all agreed with enthusiasm — including Nathann Cohen
who joined us later on. Given the success of the French version, it was clear
that an English version would be welcome. In March 2017, I decided to start
working on the English version; I want to thank once again those of the “dream
team” who helped me translating the text into English, updating the examples
to the new version of Sage, and moreover improving the content of the book
iv

(Guillaume Connan, Thierry Dumont, Clément Pernet, Nicolas Thiéry), as well


as the new authors of the English version (Erik Bray, John Cremona, Marcelo
Forets, Alexandru Ghitza, Hugh Thomas).
Several people had proof-read the French version: Gaëtan Bisson, Françoise
Jung, Hugh Thomas, Anne Vaugon, Sébastien Desreux, Pierrick Gaudry, Maxime
Huet, Jean Thiéry, Muriel Shan Sei Fan, Timothy Walsh, Daniel Duparc, and
especially Kévin Rowanet and Kamel Naroun. The following people helped us to
improve the English version by proof-reading one or several chapters, or simply
reporting a typo: Fredrik Johansson, Pierre-Jean Spaenlehauer, Jacob Appelbaum,
Nick Higham, Helmut Büch, Shashank Singh, Annegret Wagler, Bruno Grenet,
Daniel S. Roche, Jeroen Demeyer, Evans Doe Ocansey, Minh Van Nguyen, Simon
Willerton, and last but not least Adil Hasan and Dimitris Papachristoudis for their
wonderful feedback. On the technical and typographic side, we thank Emmanuel
Thomé, Sylvain Chevillard, Gaëtan Bisson, Jérémie Detrey and Denis Roegel.
When writing this book, we have learned a lot about Sage, and we have of
course encountered some bugs — some of which have already been fixed. We
hope this book will be also useful to others, high school students, undergraduate
or graduate students, engineers, researchers or simply mathematical hobbyists.
Despite several proof-readings, this book is surely not perfect, and we expect the
reader to tell us about any error, typo or make any suggestion, by referring to
the page sagebook.gforge.inria.fr.

Nancy, France
Paul Zimmermann
May 2018
Contents

I Getting to Grips with Sage 1

1 First Steps 3
1.1 The Sage Program . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.1 A Tool for Mathematics . . . . . . . . . . . . . . . . . . 3
1.2 Sage as a Calculator . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.1 First Computations . . . . . . . . . . . . . . . . . . . . 7
1.2.2 Elementary Functions and Usual Constants . . . . . . . 10
1.2.3 On-Line Help and Automatic Completion . . . . . . . . . 11
1.2.4 Python Variables . . . . . . . . . . . . . . . . . . . . . . 12
1.2.5 Symbolic Variables . . . . . . . . . . . . . . . . . . . . . 13
1.2.6 First Graphics . . . . . . . . . . . . . . . . . . . . . . . 15

2 Analysis and Algebra 17


2.1 Symbolic Expressions and Simplification . . . . . . . . . . . . . 17
2.1.1 Symbolic Expressions . . . . . . . . . . . . . . . . . . . 17
2.1.2 Transforming Expressions . . . . . . . . . . . . . . . . . 18
2.1.3 Usual Mathematical Functions . . . . . . . . . . . . . . 20
2.1.4 Assumptions . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.1.5 Some Pitfalls . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2 Equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.2.1 Explicit Solving . . . . . . . . . . . . . . . . . . . . . . . 23
2.2.2 Equations with no Explicit Solution . . . . . . . . . . . 26
2.3 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.3.1 Sums . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.3.2 Limits . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.3.3 Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.3.4 Power Series Expansions . . . . . . . . . . . . . . . . . . 30
2.3.5 Series . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.3.6 Derivatives . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.3.7 Partial Derivatives . . . . . . . . . . . . . . . . . . . . . 33
2.3.8 Integrals . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.4 Basic Linear Algebra . . . . . . . . . . . . . . . . . . . . . . . . 35
2.4.1 Solving Linear Systems . . . . . . . . . . . . . . . . . . 35
2.4.2 Vector Computations . . . . . . . . . . . . . . . . . . . 35
2.4.3 Matrix Computations . . . . . . . . . . . . . . . . . . . 36
vi CONTENTS

2.4.4 Reduction of a Square Matrix . . . . . . . . . . . . . . . 37

3 Programming and Data Structures 41


3.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.1.1 General Syntax . . . . . . . . . . . . . . . . . . . . . . . . 41
3.1.2 Function Calls . . . . . . . . . . . . . . . . . . . . . . . 43
3.1.3 More About Variables . . . . . . . . . . . . . . . . . . . 43
3.2 Algorithmics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.2.1 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.2.2 Conditionals . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.2.3 Procedures and Functions . . . . . . . . . . . . . . . . . 52
3.2.4 Example: Fast Exponentiation . . . . . . . . . . . . . . 55
3.2.5 Input and Output . . . . . . . . . . . . . . . . . . . . . 58
3.3 Lists and Other Data Structures . . . . . . . . . . . . . . . . . . 59
3.3.1 List Creation and Access . . . . . . . . . . . . . . . . . 59
3.3.2 Global List Operations . . . . . . . . . . . . . . . . . . . . 61
3.3.3 Main Methods on Lists . . . . . . . . . . . . . . . . . . 65
3.3.4 Examples of List Manipulations . . . . . . . . . . . . . . 67
3.3.5 Character Strings . . . . . . . . . . . . . . . . . . . . . . 68
3.3.6 Shared or Duplicated Data Structures . . . . . . . . . . 69
3.3.7 Mutable and Immutable Data Structures . . . . . . . . 70
3.3.8 Finite Sets . . . . . . . . . . . . . . . . . . . . . . . . . . 71
3.3.9 Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . 72

4 Graphics 75
4.1 2D Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.1.1 Graphical Representation of a Function . . . . . . . . . 75
4.1.2 Parametric Curve . . . . . . . . . . . . . . . . . . . . . 78
4.1.3 Curve in Polar Coordinates . . . . . . . . . . . . . . . . 78
4.1.4 Curve Defined by an Implicit Equation . . . . . . . . . 79
4.1.5 Data Plot . . . . . . . . . . . . . . . . . . . . . . . . . . 79
4.1.6 Displaying Solutions of Differential Equations . . . . . . 82
4.1.7 Evolute of a Curve . . . . . . . . . . . . . . . . . . . . . 88
4.2 3D Curves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

5 Computational Domains 95
5.1 Sage is Object-Oriented . . . . . . . . . . . . . . . . . . . . . . . 95
5.1.1 Objects, Classes and Methods . . . . . . . . . . . . . . . 95
5.1.2 Objects and Polymorphism . . . . . . . . . . . . . . . . 97
5.1.3 Introspection . . . . . . . . . . . . . . . . . . . . . . . . 98
5.2 Elements, Parents, Categories . . . . . . . . . . . . . . . . . . . 99
5.2.1 Elements and Parents . . . . . . . . . . . . . . . . . . . 99
5.2.2 Constructions . . . . . . . . . . . . . . . . . . . . . . . . 100
5.2.3 Further Reading: Categories . . . . . . . . . . . . . . . . 101
5.3 Domains with a Normal Form . . . . . . . . . . . . . . . . . . . . 101
5.3.1 Elementary Domains . . . . . . . . . . . . . . . . . . . . 103
5.3.2 Compound Domains . . . . . . . . . . . . . . . . . . . . 107
CONTENTS vii

5.4 Expressions vs Computational Domains . . . . . . . . . . . . . . 109


5.4.1 Symbolic Expressions as a Computational Domain . . . 109
5.4.2 Examples: Polynomials and Normal Forms . . . . . . . 109
5.4.3 Example: Polynomial Factorisation . . . . . . . . . . . . 110
5.4.4 Synthesis . . . . . . . . . . . . . . . . . . . . . . . . . . 112

II Algebra and Symbolic Computation 113

6 Finite Fields and Number Theory 115


6.1 Finite Fields and Rings . . . . . . . . . . . . . . . . . . . . . . . 115
6.1.1 The Ring of Integers Modulo n . . . . . . . . . . . . . . 115
6.1.2 Finite Fields . . . . . . . . . . . . . . . . . . . . . . . . 117
6.1.3 Rational Reconstruction . . . . . . . . . . . . . . . . . . 118
6.1.4 The Chinese Remainder Theorem . . . . . . . . . . . . 119
6.2 Primality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
6.3 Factorisation and Discrete Logarithms . . . . . . . . . . . . . . 123
6.4 Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
6.4.1 The Constant δ . . . . . . . . . . . . . . . . . . . . . . . 124
6.4.2 Computation of a Multiple Integral . . . . . . . . . . . . 125

7 Polynomials 127
7.1 Polynomial Rings . . . . . . . . . . . . . . . . . . . . . . . . . . 128
7.1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . 128
7.1.2 Building Polynomial Rings . . . . . . . . . . . . . . . . 128
7.1.3 Polynomials . . . . . . . . . . . . . . . . . . . . . . . . . 130
7.2 Euclidean Arithmetic . . . . . . . . . . . . . . . . . . . . . . . . 134
7.2.1 Divisibility . . . . . . . . . . . . . . . . . . . . . . . . . 134
7.2.2 Ideals and Quotients . . . . . . . . . . . . . . . . . . . . 136
7.3 Factorisation and Roots . . . . . . . . . . . . . . . . . . . . . . . 137
7.3.1 Factorisation . . . . . . . . . . . . . . . . . . . . . . . . 137
7.3.2 Root Finding . . . . . . . . . . . . . . . . . . . . . . . . 139
7.3.3 Resultant . . . . . . . . . . . . . . . . . . . . . . . . . . 140
7.3.4 Galois Group . . . . . . . . . . . . . . . . . . . . . . . . 142
7.4 Rational Functions . . . . . . . . . . . . . . . . . . . . . . . . . 142
7.4.1 Construction and Basic Properties . . . . . . . . . . . . 142
7.4.2 Partial Fraction Decomposition . . . . . . . . . . . . . . 143
7.4.3 Rational Reconstruction . . . . . . . . . . . . . . . . . . 144
7.5 Formal Power Series . . . . . . . . . . . . . . . . . . . . . . . . . 147
7.5.1 Operations on Truncated Power Series . . . . . . . . . . 148
7.5.2 Solutions of an Equation: Series Expansions . . . . . . . 149
7.5.3 Lazy Power Series . . . . . . . . . . . . . . . . . . . . . 150
7.6 Computer Representation of Polynomials . . . . . . . . . . . . . . 151

8 Linear Algebra 155


8.1 Elementary Constructs and Manipulations . . . . . . . . . . . . 155
8.1.1 Spaces of Vectors and Matrices . . . . . . . . . . . . . . 155
viii CONTENTS

8.1.2 Vector and Matrix Construction . . . . . . . . . . . . . 157


8.1.3 Basic Manipulations and Arithmetic on Matrices . . . . 158
8.1.4 Basic Operations on Matrices . . . . . . . . . . . . . . . 160
8.2 Matrix Computations . . . . . . . . . . . . . . . . . . . . . . . . 160
8.2.1 Gaussian Elimination, Echelon Form . . . . . . . . . . . . 161
8.2.2 Linear System Solving, Image and Nullspace Basis . . . 168
8.2.3 Eigenvalues, Jordan Form and Similarity Transformation 169

9 Polynomial Systems 179


9.1 Polynomials in Several Variables . . . . . . . . . . . . . . . . . . 179
9.1.1 The Rings A[x1 , . . . , xn ] . . . . . . . . . . . . . . . . . . 179
9.1.2 Polynomials . . . . . . . . . . . . . . . . . . . . . . . . . . 181
9.1.3 Basic Operations . . . . . . . . . . . . . . . . . . . . . . 182
9.1.4 Arithmetic . . . . . . . . . . . . . . . . . . . . . . . . . 183
9.2 Polynomial Systems and Ideals . . . . . . . . . . . . . . . . . . . 184
9.2.1 A First Example . . . . . . . . . . . . . . . . . . . . . . 184
9.2.2 What Does Solving Mean? . . . . . . . . . . . . . . . . 187
9.2.3 Ideals and Systems . . . . . . . . . . . . . . . . . . . . . 187
9.2.4 Elimination . . . . . . . . . . . . . . . . . . . . . . . . . 192
9.2.5 Zero-Dimensional Systems . . . . . . . . . . . . . . . . . 198
9.3 Gröbner Bases . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
9.3.1 Monomial Orders . . . . . . . . . . . . . . . . . . . . . . 203
9.3.2 Division by a Family of Polynomials . . . . . . . . . . . 204
9.3.3 Gröbner Bases . . . . . . . . . . . . . . . . . . . . . . . 205
9.3.4 Gröbner Basis Properties . . . . . . . . . . . . . . . . . 208
9.3.5 Computations . . . . . . . . . . . . . . . . . . . . . . . . . 211

10 Differential Equations and Recurrences 215


10.1 Differential Equations . . . . . . . . . . . . . . . . . . . . . . . . 215
10.1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . 215
10.1.2 First-Order Ordinary Differential Equations . . . . . . . 216
10.1.3 Second-Order Equations . . . . . . . . . . . . . . . . . . 223
10.1.4 The Laplace Transform . . . . . . . . . . . . . . . . . . 225
10.1.5 Systems of Linear Differential Equations . . . . . . . . . 226
10.2 Recurrence Relations . . . . . . . . . . . . . . . . . . . . . . . . 228
10.2.1 Recurrences un+1 = f (un ) . . . . . . . . . . . . . . . . . 228
10.2.2 Linear Recurrences with Rational Coefficients . . . . . . . 231
10.2.3 Non-Homogeneous Linear Recurrence Relations . . . . . . 231

III Numerical Computation 233

11 Floating-Point Numbers 235


11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
11.1.1 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . 235
11.1.2 Properties and Examples . . . . . . . . . . . . . . . . . 236
11.1.3 Standardisation . . . . . . . . . . . . . . . . . . . . . . . 236
CONTENTS ix

11.2 The Floating-Point Numbers . . . . . . . . . . . . . . . . . . . . 237


11.2.1 Which Kind of Floating-Point Numbers to Choose? . . 238
11.3 Properties of Floating-Point Numbers . . . . . . . . . . . . . . . 239
11.3.1 These Sets are Full of Gaps . . . . . . . . . . . . . . . . 239
11.3.2 Rounding . . . . . . . . . . . . . . . . . . . . . . . . . . 240
11.3.3 Some Properties . . . . . . . . . . . . . . . . . . . . . . 240
11.3.4 Complex Floating-Point Numbers . . . . . . . . . . . . 245
11.3.5 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
11.4 Interval and Ball Arithmetic . . . . . . . . . . . . . . . . . . . . 246
11.4.1 Implementation in Sage . . . . . . . . . . . . . . . . . . 247
11.4.2 Computing with Real Intervals and Real Balls . . . . . 250
11.4.3 Some Examples of Applications . . . . . . . . . . . . . . . 251
11.4.4 Complex Intervals and Complex Balls . . . . . . . . . . 253
11.4.5 Usage and Limitations . . . . . . . . . . . . . . . . . . . 254
11.4.6 Interval Arithmetic is Used by Sage . . . . . . . . . . . 254
11.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254

12 Non-Linear Equations 257


12.1 Algebraic Equations . . . . . . . . . . . . . . . . . . . . . . . . . 257
12.1.1 The Method Polynomial.roots() . . . . . . . . . . . . 257
12.1.2 Representation of Numbers . . . . . . . . . . . . . . . . 258
12.1.3 The Fundamental Theorem of Algebra . . . . . . . . . . 259
12.1.4 Distribution of the Roots . . . . . . . . . . . . . . . . . 259
12.1.5 Solvability in Radicals . . . . . . . . . . . . . . . . . . . 260
12.1.6 The Method Expression.roots() . . . . . . . . . . . . 262
12.2 Numerical Solution . . . . . . . . . . . . . . . . . . . . . . . . . 263
12.2.1 Location of Solutions of Algebraic Equations . . . . . . 264
12.2.2 Iterative Approximation Methods . . . . . . . . . . . . . 265

13 Numerical Linear Algebra 279


13.1 Inexact Computations . . . . . . . . . . . . . . . . . . . . . . . . 279
13.1.1 Matrix Norms and Condition Number . . . . . . . . . . 280
13.2 Dense Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
13.2.1 Solving Linear Systems . . . . . . . . . . . . . . . . . . 283
13.2.2 Direct Resolution . . . . . . . . . . . . . . . . . . . . . . 283
13.2.3 The LU Decomposition . . . . . . . . . . . . . . . . . . 284
13.2.4 The Cholesky Decomposition . . . . . . . . . . . . . . . 285
13.2.5 The QR Decomposition . . . . . . . . . . . . . . . . . . 286
13.2.6 Singular Value Decomposition . . . . . . . . . . . . . . . 286
13.2.7 Application to Least Squares . . . . . . . . . . . . . . . 287
13.2.8 Eigenvalues, Eigenvectors . . . . . . . . . . . . . . . . . 290
13.2.9 Polynomial Curve Fitting: the Devil is Back . . . . . . 295
13.2.10 Implementation and Efficiency . . . . . . . . . . . . . . 298
13.3 Sparse Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
13.3.1 Where do Sparse Systems Come From? . . . . . . . . . 299
13.3.2 Sparse Matrices in Sage . . . . . . . . . . . . . . . . . . 300
13.3.3 Solving Linear Systems . . . . . . . . . . . . . . . . . . 300
x CONTENTS

13.3.4 Eigenvalues, Eigenvectors . . . . . . . . . . . . . . . . . 302


13.3.5 More Thoughts on Solving Large Non-Linear Systems . 303

14 Numerical Integration 305


14.1 Numerical Integration . . . . . . . . . . . . . . . . . . . . . . . . 305
14.1.1 Available Integration Functions . . . . . . . . . . . . . . . 311
14.1.2 Multiple Integrals . . . . . . . . . . . . . . . . . . . . . 317
14.2 Solving Differential Equations . . . . . . . . . . . . . . . . . . . 318
14.2.1 An Example . . . . . . . . . . . . . . . . . . . . . . . . 319
14.2.2 Available Functions . . . . . . . . . . . . . . . . . . . . . . 321

IV Combinatorics 325

15 Enumeration and Combinatorics 327


15.1 Initial Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
15.1.1 Poker and Probability . . . . . . . . . . . . . . . . . . . 328
15.1.2 Enumeration of Trees Using Generating Functions . . . 330
15.2 Common Enumerated Sets . . . . . . . . . . . . . . . . . . . . . 336
15.2.1 First Example: Subsets of a Set . . . . . . . . . . . . . . 336
15.2.2 Integer Partitions . . . . . . . . . . . . . . . . . . . . . . 338
15.2.3 Some Other Finite Enumerated Sets . . . . . . . . . . . 340
15.2.4 Set Comprehension and Iterators . . . . . . . . . . . . . 343
15.3 Constructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
15.4 Generic Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . 351
15.4.1 Lexicographic Generation of Lists of Integers . . . . . . . 351
15.4.2 Integer Points in Polytopes . . . . . . . . . . . . . . . . 353
15.4.3 Species, Decomposable Combinatorial Classes . . . . . . 354
15.4.4 Objects up to Isomorphism . . . . . . . . . . . . . . . . 356

16 Graph Theory 363


16.1 Constructing Graphs . . . . . . . . . . . . . . . . . . . . . . . . 363
16.1.1 Starting from Scratch . . . . . . . . . . . . . . . . . . . 363
16.1.2 Available Constructors . . . . . . . . . . . . . . . . . . . 365
16.1.3 Disjoint Unions . . . . . . . . . . . . . . . . . . . . . . . 368
16.1.4 Graph Visualisation . . . . . . . . . . . . . . . . . . . . 369
16.2 Methods of the Graph Class . . . . . . . . . . . . . . . . . . . . 372
16.2.1 Modification of Graph Structure . . . . . . . . . . . . . 372
16.2.2 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . 372
16.2.3 Graph Traversal and Distances . . . . . . . . . . . . . . 374
16.2.4 Flows, Connectivity, Matching . . . . . . . . . . . . . . 375
16.2.5 NP-Complete Problems . . . . . . . . . . . . . . . . . . 376
16.2.6 Recognition and Testing of Properties . . . . . . . . . . 377
16.3 Graphs in Action . . . . . . . . . . . . . . . . . . . . . . . . . . 379
16.3.1 Greedy Vertex Colouring of a Graph . . . . . . . . . . . 379
16.3.2 Generating Graphs Under Constraints . . . . . . . . . . . 381
16.3.3 Find a Large Independent Set . . . . . . . . . . . . . . . 382
CONTENTS xi

16.3.4 Find an Induced Subgraph in a Random Graph . . . . . 383


16.4 Some Problems Solved Using Graphs . . . . . . . . . . . . . . . 385
16.4.1 A Quiz from the French Journal “Le Monde 2” . . . . . 385
16.4.2 Task Assignment . . . . . . . . . . . . . . . . . . . . . . 386
16.4.3 Plan a Tournament . . . . . . . . . . . . . . . . . . . . . 387

17 Linear Programming 389


17.1 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
17.2 Integer Programming . . . . . . . . . . . . . . . . . . . . . . . . 390
17.3 In Practice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
17.3.1 The MixedIntegerLinearProgram Class . . . . . . . . . 390
17.3.2 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
17.3.3 Infeasible or Unbounded Problems . . . . . . . . . . . . 392
17.4 First Applications in Combinatorics . . . . . . . . . . . . . . . . 393
17.4.1 Knapsack . . . . . . . . . . . . . . . . . . . . . . . . . . 393
17.4.2 Matching . . . . . . . . . . . . . . . . . . . . . . . . . . 394
17.4.3 Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
17.5 Generating Constraints and Application . . . . . . . . . . . . . 397

Annexes 405

A Answers to Exercises 405


A.1 First Steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
A.2 Analysis and Algebra . . . . . . . . . . . . . . . . . . . . . . . . 405
A.4 Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
A.5 Computational Domains . . . . . . . . . . . . . . . . . . . . . . 417
A.6 Finite Fields and Number Theory . . . . . . . . . . . . . . . . . 419
A.7 Polynomials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
A.8 Linear Algebra . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
A.9 Polynomial Systems . . . . . . . . . . . . . . . . . . . . . . . . . 429
A.10 Differential Equations and Recurrences . . . . . . . . . . . . . . 432
A.11 Floating-Point Numbers . . . . . . . . . . . . . . . . . . . . . . 434
A.12 Non-Linear Equations . . . . . . . . . . . . . . . . . . . . . . . . 437
A.13 Numerical Linear Algebra . . . . . . . . . . . . . . . . . . . . . 440
A.14 Numerical Integration . . . . . . . . . . . . . . . . . . . . . . . . . 441
A.15 Enumeration and Combinatorics . . . . . . . . . . . . . . . . . . 442
A.16 Graph Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
A.17 Linear Programming . . . . . . . . . . . . . . . . . . . . . . . . 449

B Bibliography 451

C Index 455
xii CONTENTS
Part I

Getting to Grips with Sage


1
First Steps

This introductory chapter presents the way the Sage mathematical system thinks.
The next chapters of this first part develop the basic notions: how to make symbolic
or numerical computations in analysis, how to work with vectors or matrices,
write programs, deal with data lists, produce graphics, etc. The following parts
of this book treat in more detail some branches of mathematics where computers
are very helpful.

1.1 The Sage Program


1.1.1 A Tool for Mathematics
Sage is a piece of software implementing mathematical algorithms in a variety of
contexts. To start with, it can be used as a scientific pocket calculator, and can
manipulate all sorts of numbers, from integers and rational numbers to numerical
approximations of real and complex numbers with arbitrary precision, and also
including elements of finite fields.
However, mathematical computations go far beyond numbers: Sage is a
computer algebra system; it can for example help junior high school students
learn how to solve linear equations, or develop, factor, or simplify expressions; or
carry out such operations in arbitrary rings of polynomials or rational function
fields. In analysis, Sage can manipulate expressions involving square roots,
exponentials, logarithms or trigonometric functions: integration, computation
of limits, simplification of sums, series expansion, solution of certain differential
equations, and more. In linear algebra it computes with vectors, matrices, and
subspaces. It can also help illustrate and solve problems in probability, statistics,
and combinatorics.
4 CHAP. 1. FIRST STEPS

To summarise, Sage strives to provide a consistent and uniform access to


features in a wide area of mathematics — ranging from group theory to numerical
analysis — and beyond — visualisation in two and three dimensions, animation,
networking, databases, ... Using a single unified piece of software frees the
(budding) mathematician from having to transfer data between several tools and
learn the syntax of several programming languages.

Access to Sage

To use Sage, all that is needed is a web browser. As a starter, the ser-
vice http://sagecell.sagemath.org/ allows for testing commands. To
go further, one can use one of the online services. For example, CoCalc
(http://cocalc.com, formerly known as SageMathCloud) gives access to a
lot of computational software and collaborative tools, together with course
management features. Developed and hosted by SageMathInc, an indepen-
dent company founded by William Stein, its access is free for casual use, and
most of its code is free. Other similar services are hosted by universities and
institutions. Ask around to find out what is available near you.
For regular usage, it is recommended to use Sage on one’s own machine,
installing it if this has not yet been done by the system administrator. Sage
is available for most operating systems: Linux, Windows, MacOS; see the
Download section on http://sagemath.org.
How to start Sage depends on the environment; therefore we do not go
into details here. On CoCalc one needs to create an account, a project, and
finally a Jupyter worksheet. On a desktop, the system may provide a startup
icon. Under Linux or MacOS, one typically would launch the command sage
––notebook jupyter in a terminal.

Resources

The official Sage website offers many resources:


http://www.sagemath.org/ official site
http://doc.sagemath.org/ documentation
http://wiki.sagemath.org/quickref command lists
To get help on using Sage, the Question and Answer site http:
//ask.sagemath.org/ is very active. For technical questions (in-
stallation, troubleshooting, ...), the best medium is the mailing list
sage-support@googlegroups.com.
1.1. THE SAGE PROGRAM 5

User interfaces: notebooks or command line

However Sage is accessed, one can use it via a web application enabling the
edition and sharing of notebooks which mix code, interactive computations,
equations, visualisations and text:

The Help menu gives access to the documentation. We recommend


starting with the User Interface Tour, returning often to the Keyboard
Shortcuts, and progressively exploring the Thematic Tutorials.
Sage uses Jupyter as web application. Formerly known as IPython,
Jupyter allows the use of a great deal of mathematical software (GAP,
PARI/GP, or Singular, ...) and beyond (from Python to C++!), and is
supported by a large community. Notebooks are respectively in the .sws and
.ipynb format. CoCalc offers another format .sagews which is less portable,
but explores advanced interaction features.
As an alternative, one can use Sage in a terminal. Its calculator-like
command line interface gives full access to all of its capabilities, including
graphics:
6 CHAP. 1. FIRST STEPS

Sage and Python

Like most software for mathematical computations, Sage is used by


issuing commands written in a programming language. For this purpose,
Sage uses the general purpose programming language Python, with just a tiny
layer of syntactic sugar to support some common mathematical notations
in interactive use. For complicated or just repetitive calculations, one can
write programs instead of simple one-line commands. When mature and of
general interest, such programs can be submitted for inclusion in Sage.

Aims and history of Sage

In 2005, William Stein, an American academic, initiated the Sage project,


with the goal of producing free software for mathematical computation, devel-
oped by users for users. This meets a longstanding need of mathematicians,
and soon an international community of hundreds of developers crystallised
around Sage, most of them teachers and researchers. At first Sage had some
focus on number theory, the area of interest of its founder. As contributions
flowed in, its capabilities progressively extended to many areas of mathemat-
ics. This, together with the numerical capabilities brought in by the Scientific
Python ecosystem, has made Sage the general purpose mathematics software
that it is today.
Not only can Sage be used and downloaded for free, but it is free software:
the authors impose no restriction on its usage, redistribution, study or
modification, as long as the modifications are free themselves. In the same
spirit, the material in this book can be freely read, shared, and reused (with
proper credit, of course). This license is in harmony with the spirit of free
development and dissemination of knowledge in academia.

Sage, a software in an ecosystem

The development of Sage was relatively quick thanks to its strategy


of reusing existing free software, including many specialised mathematical
libraries or systems like GAP, PARI/GP, Maxima, Singular, to cite just a
few.
Sage itself is written in Python, a programming language used by
millions and known for the ease with which it can be learned. Python
is particularly well established in the sciences. Within the same com-
puting environment, it is possible to combine the capabilities of Sage
with scientific libraries for numerical computations, data analysis, statis-
tics, visualisation, machine learning, biology, astrophysics, and technical
libraries for networking, databases, web, ... See for example: https:
//en.wikipedia.org/wiki/List_of_Python_software.
1.2. SAGE AS A CALCULATOR 7

SAGE, Sage, or SageMath?

Figure 1.1 – The first occurrence of the name Sage, on a handwritten note of
W. Stein.
At first, Sage was both an acronym and a reference to the “sage” medicinal
plant. When the system later expanded to cover much of mathematics, the
acronym part was dropped. As Sage came to be known in larger circles,
and to avoid confusion with, for example, the business management software
of the same name, the official name was changed to SageMath. When the
context is unambiguous, for example in this book, it is traditional to just
use Sage.

1.2 Sage as a Calculator


1.2.1 First Computations
In the rest of the book, we present computations in the following form, which
mimics a command line Sage session:
sage: 1+1
2
The sage: text in the beginning of the first line is the command prompt of the
system. The prompt (which does not appear in the notebook interface) means
that Sage awaits a user command. The rest of the line is the command to execute,
which is validated with the hEnteri key. The lines below are the system’s answer,
which in general are the results of the computation. Some commands use several
lines (see Chapter 3). The additional command lines can then be recognised
by .... at the beginning of the line. A multi-line command should follow the
position of linebreaks and the indentation (spaces to align the line with respect
to the previous one), without copying the initial .....
In the notebook, one directly enters the commands in a computation cell, and
validates by clicking on evaluate or using the hShifti+hEnteri key combination.
The combination hAlti+hEnteri not only executes the command of the current
cell, but also creates a new cell just below. One can also create a new cell by
clicking in the small space just above a given cell, or below the last cell.
Sage interprets simple formulas like a scientific calculator. The operations +,
×, etc. have their usual precedence, and parentheses their common usage:
8 CHAP. 1. FIRST STEPS

sage: ( 1 + 2 * (3 + 5) ) * 2
34

The * character above stands for multiplication, which should not be omitted,
even in expressions like 2x. The power operation is written ˆ or **:
sage: 2^3
8
sage: 2**3
8

and the division is denoted by /:


sage: 20/6
10/3

Please note the exact computation: the result of the above division, after sim-
plification, is the rational number 10/3 and not an approximation like 3.33333.
There is no limit1 to the size of integers or rational numbers:
sage: 2^10
1024
sage: 2^100
1267650600228229401496703205376
sage: 2^1000
1071508607186267320948425049060001810561404811705533607443750\
3883703510511249361224931983788156958581275946729175531468251\
8714528569231404359845775746985748039345677748242309854210746\
0506237114187795418215304647498358194126739876755916554394607\
7062914571196477686542167660429831652624386837205668069376

To obtain a numerical approximation, one simply writes one of the numbers


with a decimal point (one could replace 20.0 by 20. or 20.000):
sage: 20.0 / 14
1.42857142857143

Besides, the numerical_approx function gives a numerical approximation of an


expression:
sage: numerical_approx(20/14)
1.42857142857143
sage: numerical_approx(2^1000)
1.07150860718627e301

Numerical approximations can be computed to arbitrarily large precisions. For


example, let us increase the precision to 60 digits to exhibit the periodicity of the
digit expansion of a rational number:
1 Except that due to the available memory of the computer used.
1.2. SAGE AS A CALCULATOR 9

Basic arithmetic operations


“four operations” a+b, a-b, a*b, a/b
power a^b or a**b
square root sqrt(a)
n-th root a^(1/n)

Integer operations
integer division a // b
remainder a % b
quotient and remainder divmod(a,b)
factorial n! factorial(n)
binomial coefficient nk
binomial(n,k)

Usual functions on real numbers, complex numbers, ...


integer part floor(a)
absolute value, modulus abs(a)
elementary functions sin, cos, ... (see Table 2.2)

Table 1.1 – Some usual operations.

sage: numerical_approx(20/14, digits=60)


1.42857142857142857142857142857142857142857142857142857142857
Differences between exact and numerical computations are discussed in the sidebar
on page 10.
The operators // and % yield the quotient and remainder of the division of
two integers:
sage: 20 // 6
3
sage: 20 % 6
2
Several other functions apply to integers. Among those specific to integers are
the factorial and the binomial coefficients (see Table 1.1):
sage: factorial(100)
93326215443944152681699238856266700490715968264381621\
46859296389521759999322991560894146397615651828625369\
7920827223758251185210916864000000000000000000000000
Here is a way to decompose an integer into prime factors. We will return to this
problem in Chapter 5, then once more in Chapter 6.
sage: factor(2^(2^5)+1)
641 * 6700417
n
Fermat had conjectured that all integers 22 + 1 are prime. The above example
is the smallest counter-example.
10 CHAP. 1. FIRST STEPS

Computer algebra and numerical methods

A computer algebra system is a program made to manipulate, simplify


and compute mathematical formulas by applying only exact (i.e., symbolic)
transformations. The term symbolic is opposed here to numerical; it means
that computations are made using algebraic formulas, manipulating symbols
only. This is why symbolic computation is sometimes used in place of computer
algebra. In French, one says calcul formel or sometimes calcul symbolique.
In general, pocket calculators manipulate integers exactly up to twelve
digits; larger numbers are rounded, which induces errors. Thus a pocket
calculator wrongly evaluates to 0 the following expression, whereas the correct
result is 1:
(1 + 1050 ) − 1050 .
Such errors are difficult to detect if they arise during an intermediate compu-
tation, without being anticipated by a theoretical analysis. On the contrary,
computer algebra systems do not have these limitations, and perform all
integer computations exactly: they answer 1 to the previous computation.
Numerical methods approximate to a given precision (using the trape-
zoidal
Rπ rule, Simpson’s rule, Gaussian quadrature, etc.) the definite integral
0
cos t dt to obtain a numerical result near zero (with error 10−10 for exam-
ple). However, they cannot tell the user if the result is exactly 0, or on the
contrary is near zero but definitively not zero.
A computer algebra Rsystem rewrites using symbolic mathematical trans-
π
formations the integral 0 cos t dt into the expression sin π − sin 0, which is

then evaluated into 0 − 0 = 0. This method proves whence 0 cos t dt = 0.
However, algebraic transformations have limits too. Most expressions
handled by symbolic computation systems are rational functions, and the ex-
pression a/a is automatically simplified into 1. This automatic simplification
is not compatible with solving equations; indeed, the solution to the equation
ax = a is x = a/a, which is simplified into x = 1 without distinguishing the
special case a = 0, for which any scalar x is solution (see also §2.1.5).

1.2.2 Elementary Functions and Usual Constants


The usual functions and constants are available (see Tables 1.1 and 1.2), as well
as for complex numbers. Here also, computations are exact:
sage: sin(pi)
0
sage: tan(pi/3)
sqrt(3)
sage: arctan(1)
1/4*pi
sage: exp(2 * I * pi)
1
even if symbolic expressions are returned instead of numerical expressions:
1.2. SAGE AS A CALCULATOR 11

Some specials values


boolean values “true” and “false” True, False
imaginary unit i I or i
infinity ∞ Infinity or oo

Common mathematical constants


Archimedes’ constant π pi
logarithm basis e = exp(1) e
Euler-Mascheroni constant
√ γ euler_gamma
golden ratio ϕ = (1 + 5)/2 golden_ratio
Catalan’s constant catalan

Table 1.2 – Predefined constants.

sage: arccos(sin(pi/3))
arccos(1/2*sqrt(3))
sage: sqrt(2)
sqrt(2)
sage: exp(I*pi/7)
e^(1/7*I*pi)
One does not always get the expected results. Indeed, only few simplifications
are done automatically. If needed, it is possible to explicitly call a simplification
function:
sage: simplify(arccos(sin(pi/3)))
1/6*pi
We will see in §2.1 how to tune the simplification of expressions. Of course, it
is also possible to compute numerical approximations of the results, with an
accuracy as large as desired:
sage: numerical_approx(6*arccos(sin(pi/3)), digits=60)
3.14159265358979323846264338327950288419716939937510582097494
sage: numerical_approx(sqrt(2), digits=60)
1.41421356237309504880168872420969807856967187537694807317668

1.2.3 On-Line Help and Automatic Completion


The reference manual of each function, constant or command is accessed via the
question mark ? after its name:
sage: sin?
The documentation page contains the function description, its syntax and some
examples of usage.
The tabulation key hTabi after the beginning of a word yields all command
names starting with these letters: thus arc followed by hTabi prints the name of
all inverse trigonometric and hyperbolic functions:
12 CHAP. 1. FIRST STEPS

sage: arc<tab>
Possible completions are:
arc arccos arccosh arccot arccoth arccsc arccsch
arcsec arcsech arcsin arcsinh arctan arctan2 arctanh

1.2.4 Python Variables


To save the result of a computation, one assigns it to a variable:
sage: y = 1 + 2
to reuse it later on:
sage: y
3
sage: (2 + y) * y
15
Note that the result of a computation is not automatically printed when it is
assigned to a variable. Therefore, we will do the following to also print it,
sage: y = 1 + 2; y
3
the ’;’ character separating several instructions on the same line. Since the
computation of the result is done before the assignment, one can reuse the same
variable:
sage: y = 3 * y + 1; y
10
sage: y = 3 * y + 1; y
31
sage: y = 3 * y + 1; y
94
Additionally, Sage saves the last three results in the special variables _, __ and
___:
sage: 1 + 1
2
sage: _ + 1
3
sage: __
2
The variables we have used above are Python variables; we will discuss them
further in §3.1.3. Let us just mention that it is not recommended to redefine
predefined constants and functions from Sage. While it does not influence the
internal behaviour of Sage, it could yield surprising results:
sage: pi = -I/2
sage: exp(2*I*pi)
1.2. SAGE AS A CALCULATOR 13

e
To restore the original value, one can type for example:
sage: from sage.all import pi
or alternatively
sage: restore()
which restores to their default value all predefined variables and functions. The
reset() function performs an even more complete reset, in particular it clears all
user-defined variables.

1.2.5 Symbolic Variables



We have played so far with constant expressions like sin( 2), but Sage is especially
useful in dealing with expressions containing variables like x + y + z or sin(x) +
cos(x). The “mathematician’s” symbolic variables x, y, z appearing in those
expressions differ in general from the “programmer’s” variables encountered in
the preceding section. On this point, Sage differs notably from other computer
algebra systems like Maple or Maxima.
The symbolic variables should be explicitly declared before being used2 (SR
abbreviates Symbolic Ring):
sage: z = SR.var('z')
sage: 2*z + 3
2*z + 3
In this example, the command SR.var(’z’) builds and returns a symbolic variable
whose name is z. This symbolic variable is a perfect Sage object: it is handled
exactly like more complex expressions like sin(x) + 1. Then, this symbolic variable
is assigned to the “programmer’s” variable z, which enables one to use it like any
other expression, to build more complex expressions.
We could have assigned z to another variable than z:
sage: y = SR.var('z')
sage: 2*y + 3
2*z + 3
Hence, assigning the symbolic variable z to the Python variable z is just a
convention, which is however recommended to avoid confusion.
Conversely, the Python variable z does not interact with the symbolic vari-
able z:
sage: c = 2 * y + 3
sage: z = 1
sage: 2*y + 3
2*z + 3
sage: c
2 Except the symbolic variable x, which is predefined in Sage.
14 CHAP. 1. FIRST STEPS

2*z + 3
How can we give a value to a symbolic variable appearing in an expression? One
uses the substitution operation, as in:
sage: x = SR.var('x')
sage: expr = sin(x); expr
sin(x)
sage: expr(x=1)
sin(1)
The substitution in symbolic expressions is discussed in detail in the next chapter.
Exercise 1. Explain step by step what happens during the following instructions:
sage: u = SR.var('u')
sage: u = u+1
sage: u = u+1
sage: u
u + 2

As it would become tedious to create a large number of symbolic variables,


there exists a shortcut x = SR.var(’x’, n) where n is a positive integer (notice
that indexing starts at 0):
sage: x = SR.var('x', 100)
sage: (x[0] + x[1])*x[99]
(x0 + x1)*x99
The command var(’x’) is a convenient alternative for x = SR.var(’x’)3 :
sage: var('a, b, c, x, y')
(a, b, c, x, y)
sage: a * x + b * y + c
a*x + b*y + c
If the explicit declaration of symbolic variables is too cumbersome, it is also
possible to emulate the behaviour of systems like Maxima or Maple. However,
this functionality is only available in the notebook interface (but not the Jupyter
worksheet). Thus in the notebook, after:
sage: automatic_names(True)

every use of an unassigned variable yields the creation of a symbolic variable of


the same name and its assignment:
sage: 2 * bla + 3
2*bla + 3
sage: bla
bla

3 In this book, we will often write x = var(’x’) instead of the better but cumbersome form

x = SR.var(’x’), to avoid the output produced by var(’x’).


1.2. SAGE AS A CALCULATOR 15

1.2.6 First Graphics


The plot command makes it easy to draw the curve of a real function on a given
interval. The plot3d command is its counterpart for three-dimensional graphics,
or for the graph of a real function of two variables. Here are examples of those
two commands:
sage: plot(sin(2*x), x, -pi, pi)
sage: plot3d(sin(pi*sqrt(x^2 + y^2))/sqrt(x^2+y^2),
....: (x,-5,5), (y,-5,5))
The graphical capacities of Sage are much wider. We will explore them in more
detail in Chapter 4.
16 CHAP. 1. FIRST STEPS
Analysis and Algebra
2
This chapter uses simple examples to describe the useful basic functions in analysis
and algebra. Students will be able to replace pen and paper by keyboard and screen
while keeping the same intellectual challenge of understanding mathematics.
This presentation of the main calculus commands with Sage should be ac-
cessible to young students; some parts marked with an asterisk are reserved for
higher-level students. More details are available in the other chapters.

2.1 Symbolic Expressions and Simplification


2.1.1 Symbolic Expressions
Sage allows a wide range of analytic computations on symbolic expressions
formed with numbers, symbolic variables, the four basic operations, and usual
functions like sqrt, exp, log, sin, cos, etc. A symbolic expression can be
seen as a tree like in Figure 2.1. It is important to understand that a sym-
bolic expression is a formula and not a value or a mathematical function.
Thus, Sage does not recognise the two following expressions as equal1 :

sage: bool(arctan(1+abs(x)) == pi/2 - arctan(1/(1+abs(x))))


False
Thanks to the commands presented in this chapter, the user can transform
expressions into the desired form.
1 The equality test == is not only a syntactic comparison: for example, the expressions

arctan(sqrt(2)) and pi/2-arctan(1/sqrt(2)) are considered equal. In fact, when one compares
two expressions with bool(x==y), Sage tries to prove that their difference is zero, and returns
True if that succeeds.
18 CHAP. 2. ANALYSIS AND ALGEBRA

+ *

^ * 2 + +

x 2 3 x x 1 x 2

x^2 + 3*x + 2 (x + 1)*(x + 2)

Figure 2.1 – Two symbolic expressions representing the same mathematical object.

The most common operation consists of evaluating an expression by giving a


value to some of its parameters. The subs method — which can be made implicit
— performs this transformation:
sage: a, x = var('a, x'); y = cos(x+a) * (x+1); y
(x + 1)*cos(a + x)
sage: y.subs(a=-x); y.subs(x=pi/2, a=pi/3); y.subs(x=0.5, a=2.3)
x + 1
-1/4*sqrt(3)*(pi + 2)
-1.41333351100299
sage: y(a=-x); y(x=pi/2, a=pi/3); y(x=0.5, a=2.3)
x + 1
-1/4*sqrt(3)*(pi + 2)
-1.41333351100299
Compared to the usual mathematical notation x 7→ f (x), the variable which is
substituted must be explicitly given. The substitution of several parameters is
done in parallel, while successive substitutions are performed in sequence, as
shown by the two examples below:
sage: x, y, z = var('x, y, z') ; q = x*y + y*z + z*x
sage: bool(q(x=y, y=z, z=x) == q), bool(q(z=y)(y=x) == 3*x^2)
(True, True)
To replace an expression more complex than a single variable, the substitute
method is available:
sage: y, z = var('y, z'); f = x^3 + y^2 + z
sage: f.substitute(x^3 == y^2, z==1)
2*y^2 + 1

2.1.2 Transforming Expressions


The simplest non-constant expressions are polynomials and rational functions of
one or more variables. The functions allowing to rewrite expressions in several
forms or to put them in normal form are summarised in Table 2.1. For example,
the expand method is useful to expand polynomials:
sage: x, y = SR.var('x,y')
2.1. SYMBOLIC EXPRESSIONS AND SIMPLIFICATION 19

Symbolic Functions

Sage allows also to define symbolic functions to manipulate expressions:


sage: f(x)=(2*x+1)^3 ; f(-3)
-125
sage: f.expand()
x |--> 8*x^3 + 12*x^2 + 6*x + 1
A symbolic function is just like an expression that we can call like a command
and where the order of variables is fixed. To convert a symbolic expression
into a symbolic function, we use either f(x) = ..., or the function method:

sage: y = var('y'); u = sin(x) + x*cos(y)


sage: v = u.function(x, y); v
(x, y) |--> x*cos(y) + sin(x)
sage: w(x, y) = u; w
(x, y) |--> x*cos(y) + sin(x)
Symbolic functions are useful to represent mathematical functions. They
differ from Python functions or procedures, which are programming construc-
tions described in Chapter 3. The difference between symbolic functions and
Python functions is similar to the difference between symbolic variables and
Python variables, described in §1.2.5.
A symbolic function can be used like an expression, which is not the case
for Python functions; for example, the expand method does not exist for the
latter.

sage: p = (x+y)*(x+1)^2
sage: p2 = p.expand(); p2
x^3 + x^2*y + 2*x^2 + 2*x*y + x + y
whereas the collect method groups terms together according to the powers of a
given variable:
sage: p2.collect(x)
x^3 + x^2*(y + 2) + x*(2*y + 1) + y
Those methods do not only apply to polynomials in symbolic variables, but also
to polynomials in more complex sub-expressions like sin x:
sage: ((x+y+sin(x))^2).expand().collect(sin(x))
x^2 + 2*x*y + y^2 + 2*(x + y)*sin(x) + sin(x)^2
For rational functions, the combine method enables us to group together terms
with common denominator; the partial_fraction method performs the partial
fraction decomposition over Q. (To specify a different decomposition field, we
refer the reader to §7.4.)
The more useful representations are the expanded form for a polynomial, and
the reduced form P/Q with P and Q expanded in the case of a fraction. When
20 CHAP. 2. ANALYSIS AND ALGEBRA

Polynomial p = zx2 + x2 − (x2 + y 2 )(ax − 2by) + zy 2 + y 2


p.expand() −ax3 + 2 bx2 y − axy 2 + 2 by 3 + x2 z + y 2 z + x2 + y 2
p.expand().collect(x) −ax3 − axy 2 + 2 by 3 + (2 by + z + 1)x2 + y 2 z + y 2
p.collect(x).collect(y) 2 bx2 y + 2 by 3 − (ax − z − 1)x2 − (ax − z − 1)y 2
p.factor() −(ax − 2 by − z − 1) x2 + y 2
  
p.factor_list() (ax − 2 by − z − 1, 1) , x2 + y 2 , 1 , (−1, 1)

x3 +x2 y+3 x2 +3 xy+2 x+2 y


Fraction r= x3 +2 x2 +xy+2 y
x2 +(x+1)y+x
r.simplify_rational() x2 +y
(x+y)(x+1)
r.factor() x2 +y
x2
r.factor().expand() x2 +y
+ x2xy
+y
+ x
x2 +y
+ y
x2 +y

(x−1)x y2 b c 1
Fraction r= x2 −7
+ x2 −7
+ a
+ a
+ x+1
(x−1)x+y 2 b+c 1
r.combine() x2 −7
+ a
+ x+1

1
Fraction r=
(x3 +1)y2
−(x−2) 1
r.partial_fraction(x) +
3 (x2 −x+1)y 2 3 (x+1)y 2

Table 2.1 – Polynomials and fractions.

two polynomials or fractions are written in this form, it suffices to compare their
coefficients to decide if they are equal: we say they are in normal form.

2.1.3 Usual Mathematical Functions


Most mathematical functions are known to Sage, in particular the trigonometric
functions, the logarithm and the exponential: they are summarised in Table 2.2.
Knowing how to transform such functions is crucial. To simplify an expression
or a symbolic function, the simplify method is available:
sage: (x^x/x).simplify()
x^(x - 1)
However, for more subtle simplifications, the desired kind of simplification
should be explicit:
sage: f = (e^x-1) / (1+e^(x/2)); f.canonicalize_radical()
e^(1/2*x) - 1
For example, to simplify trigonometric expressions, the simplify_trig method
should be used:
sage: f = cos(x)^6 + sin(x)^6 + 3 * sin(x)^2 * cos(x)^2
sage: f.simplify_trig()
1
2.1. SYMBOLIC EXPRESSIONS AND SIMPLIFICATION 21

Usual mathematical functions


Exponential and logarithm exp, log
Logarithm in base a log(x, a)
Trigonometric functions sin, cos, tan
Inverse trigonometric functions arcsin, arccos, arctan
Hyperbolic functions sinh, cosh, tanh
Inverse hyperbolic functions arcsinh, arccosh, arctanh
Integer part, etc. floor, ceil, trunc, round
Square and n-th root sqrt, nth_root

Rewriting trigonometric expressions


Simplification simplify_trig
Linearisation reduce_trig
Anti-linearisation expand_trig

Table 2.2 – Usual functions and simplification.

To linearise (resp. anti-linearise) a trigonometric expression, we use reduce_trig


(resp. expand_trig):
sage: f = cos(x)^6; f.reduce_trig()
1/32*cos(6*x) + 3/16*cos(4*x) + 15/32*cos(2*x) + 5/16
sage: f = sin(5 * x); f.expand_trig()
5*cos(x)^4*sin(x) - 10*cos(x)^2*sin(x)^3 + sin(x)^5
Expressions containing factorials can also be simplified:
sage: n = var('n'); f = factorial(n+1)/factorial(n)
sage: f.simplify_factorial()
n + 1
The simplify_rational method tries to simplify a fraction; whereas to simplify
square roots, logarithms or exponentials, the canonicalize_radical method is
recommended:
sage: f = sqrt(abs(x)^2); f.canonicalize_radical()
abs(x)
sage: f = log(x*y); f.canonicalize_radical()
log(x) + log(y)
The simplify_full command applies the methods simplify_factorial, simplify_
rectform, simplify_trig, simplify_rational and expand_sum (in that order).
All that is needed to determine the variation of a function (derivatives, asymp-
totes, extrema, localisation of zeroes and graph drawing) can be easily obtained
using a computer algebra system. The main Sage operations applying to functions
are presented in §2.3.

2.1.4 Assumptions
During a computation, the symbolic variables appearing in expressions are in
general considered as taking potentially any value in the complex plane. This
22 CHAP. 2. ANALYSIS AND ALGEBRA

might be a problem when a parameter represents a quantity in a restricted domain


(for example, a positive real number). √
A typical case is the simplification of the expression x2 . The proper way
consists of using the assume function, which enables us to define the properties
of a variable, which can in turn be reverted by the forget instruction:
sage: assume(x > 0); bool(sqrt(x^2) == x)
True
sage: forget(x > 0); bool(sqrt(x^2) == x)
False
sage: n = var('n'); assume(n, 'integer'); sin(n*pi)
0

2.1.5 Some Pitfalls

The Simplification Problem

The examples of §2.1.5 demonstrate how important normal forms are,


and in particular the test of zero.
Some families of expressions, like polynomials, have a decision procedure
for the equality to zero. As a consequence, for those families, a program is
able to decide whether a given expression is zero or not. In most cases, this
test is done via the reduction to the normal form: the expression is zero if
and only if its normal form is 0.
Unfortunately, not all classes of expressions have a normal form, moreover
for some classes it is possible to show that no general method is able to decide
in a finite amount of time whether an expression is zero. An example of such
a class is made of the rational numbers, the constants π, log 2 and a variable,
together with additions, subtractions, multiplications, exponentials and the
sine function. The repeated use of numerical_approx, while increasing
the precision, succeeds in most cases to conjecture if a given expression is
zero or not; however, it has been proven impossible to write a computer
program taking as input an expression of this class, and returning true if
this expression is zero, and false otherwise.
The simplification problem is much harder in those classes. Without any
normal form, computer algebra systems can only provide some rewriting
functions that the user must play with to obtain some result. Some hope is
however possible, if we can identify sub-classes of expressions which have a
normal form, and if we know which methods should be applied to compute
those normal forms. The Sage approach to handle those issues is presented
in more details in Chapter 5.

Let c be a slightly complex expression:


sage: a = var('a')
sage: c = (a+1)^2 - (a^2+2*a+1)
2.2. EQUATIONS 23

where we want to solve the equation cx = 0 in the variable x:


sage: eq = c * x == 0
One might be tempted to divide out this equation by c before solving it:
sage: eq2 = eq / c; eq2
x == 0
sage: solve(eq2, x)
[x == 0]
Fortunately, Sage avoids this mistake:
sage: solve(eq, x)
[x == x]
Sage was able to correctly solve the equation because the coefficient c is a
polynomial expression. It is thus easy to check whether c is zero, by expanding
it:
sage: expand(c)
0
and use the fact that two mathematically identical polynomials share the same
expanded form, or said otherwise, that the expanded form is a normal form for
polynomials.
However, on a slightly more complex example, Sage does not avoid the pitfall:
sage: c = cos(a)^2 + sin(a)^2 - 1
sage: eq = c*x == 0
sage: solve(eq, x)
[x == 0]
even if Sage is able to correctly simplify and test to zero this expression:
sage: c.simplify_trig()
0
sage: c.is_zero()
True

2.2 Equations
We now deal with equations and how to solve them; the main functions are
summarised in Table 2.3.

2.2.1 Explicit Solving


Let us consider the following equation, with unknown z and parameter ϕ:
2 5 i π πh
z2 − z+ − 4 = 0, with ϕ ∈ − , .
cos ϕ cos2 ϕ 2 2
It is written in Sage:
24 CHAP. 2. ANALYSIS AND ALGEBRA

Scalar equations
Symbolic solution solve
Roots (with multiplicities) roots
Numerical solving find_root

Vector and functional equations


Solving linear equations solve_right, solve_left
Solving differential equations desolve
Solving recurrences rsolve

Table 2.3 – Solving equations.

sage: z, phi = var('z, phi')


sage: eq = z**2 - 2/cos(phi)*z + 5/cos(phi)**2 - 4 == 0; eq
z^2 - 2*z/cos(phi) + 5/cos(phi)^2 - 4 == 0
We can extract the left-hand (resp. right-hand) side with the lhs (resp. rhs)
method:
sage: eq.lhs()
2z 5
z 2 − cos(ϕ) + cos(ϕ) 2 − 4

sage: eq.rhs()
0
then solve it for z with solve:

sage: solve(eq,

z)
√ 
2
2 cos(ϕ) −1−1 2 cos(ϕ)2 −1+1
z=− cos(ϕ) , z = cos(ϕ)

Let us now solve the equation y 7 = y.


sage: y = var('y'); solve(y^7==y, y)
[y == 1/2*I*sqrt(3) + 1/2, y == 1/2*I*sqrt(3) - 1/2, y == -1,
y == -1/2*I*sqrt(3) - 1/2, y == -1/2*I*sqrt(3) + 1/2, y == 1, y == 0]
The roots of the equation can be returned as an object of type dictionary (cf.
§3.3.9):
sage: solve(x^2-1, x, solution_dict=True)
[{x: -1}, {x: 1}]
The solve command can also solve systems of equations:
sage: solve([x+y == 3, 2*x+2*y == 6], x, y)
[[x == -r1 + 3, y == r1]]
This linear system being underdetermined, the variable allowing to parametrise
the set of solutions is a real number named r1, r2, etc. If this parameter is known
to be an integer, it is named z1, z2, etc. (below, z... stands for z36, z60, or
similar, according to the Sage version):
2.2. EQUATIONS 25

sage: solve([cos(x)*sin(x) == 1/2, x+y == 0], x, y)


[[x == 1/4*pi + pi*z..., y == -1/4*pi - pi*z...]]
The solve command can also solve inequalities:
sage: solve(x^2+x-1 > 0, x)
[[x < -1/2*sqrt(5) - 1/2], [x > 1/2*sqrt(5) - 1/2]]
Sometimes, solve returns the solutions of a system as floating-point numbers.
For example, let us solve in C3the following system:
 x2 yz = 18,
xy 3 z = 24,

xyz 4 = 6.
sage: x, y, z = var('x, y, z')
sage: solve([x^2 * y * z == 18, x * y^3 * z == 24,\
....: x * y * z^4 == 6], x, y, z)
[[x == 3, y == 2, z == 1],
[x == (1.337215067329613 - 2.685489874065195*I),
y == (-1.700434271459228 + 1.052864325754712*I),
z == (0.9324722294043555 - 0.3612416661871523*I)], ...]
Sage returns here 17 tuples, among which 16 are approximate complex solutions.
To obtain a fully symbolic solution, we refer to Chapter 9.
To solve equations numerically, the find_root function takes as input a
function of one variable or a symbolic equality, and the bounds of the interval in
which to search. Sage does not find any symbolic solution to this equation:
sage: expr = sin(x) + sin(2 * x) + sin(3 * x)
sage: solve(expr, x)
[sin(3*x) == -sin(2*x) - sin(x)]
Two choices are then possible: either a numerical solution,
sage: find_root(expr, 0.1, pi)
2.0943951023931957
or first rewrite the expression:
sage: f = expr.simplify_trig(); f
2*(2*cos(x)^2 + cos(x))*sin(x)
sage: solve(f, x)
[x == 0, x == 2/3*pi, x == 1/2*pi]
Last but not least, the roots function gives the roots of an equation with
their multiplicity. The ring in which solutions are looked for can be given; with
RR ≈ R or CC ≈ C, we obtain floating-point roots. The solving method is specific
to the given equation, contrary to find_roots which uses a generic method.
Let us consider the degree-3 equation x3 + 2 x + 1 = 0. This equation has a
negative discriminant, thus it has a real root and two complex roots, which are
given by the roots method:
sage: (x^3+2*x+1).roots(x)
26 CHAP. 2. ANALYSIS AND ALGEBRA

"  ( 31 ) √  
 √  √ √ − I 3−1
− 1 I 3 + 1 1
3 59 −
1
+ 
2 18 2 √ √ ( 1 ) , 1 ,
3 18 3 59 − 12 3
1
 √  
   ( 31 )
√ √ √ − −I 3 − 1
− 1 −I 3 + 1 1
3 59 −
1
+ 
2 18 2 √ √ ( 1 ) , 1 ,
1
3 18 3 59 − 21 3
 
 ( 31 )
√ √
 1 3 59 − 1 +
−2 
18 2 √ √ ( 1 ) , 1
3 18 3 59 − 21 3
1

sage: (x^3+2*x+1).roots(x, ring=RR)


[(−0.453397651516404, 1)]

sage: (x^3+2*x+1).roots(x, ring=CC)



(−0.453397651516404, 1), (0.226698825758202 − 1.46771150871022 ∗ I, 1),

(0.226698825758202 + 1.46771150871022 ∗ I, 1)

2.2.2 Equations with no Explicit Solution


In most cases, as soon as the equation or system becomes too complex, no explicit
solution can be found:

sage: solve(x^(1/x)==(1/x)^x, x)
[(1/x)^x == x^(1/x)]

However, this is not necessarily a limitation! Indeed, a specificity of computer


algebra is the ability to manipulate objects defined by equations, and in particular
to compute their properties, without solving them explicitly. Even better: in
some cases, the equation defining a mathematical object is the best algorithmic
representation for it.
For example, a function given by a linear differential equation and initial
conditions is perfectly defined. The set of solutions of linear differential equations
is closed under sum and product (among other operations), and thus forms an
important class where equality to zero can be decided. However, if we explicitly
solve such an equation, the obtained solution might be part of a much larger class
where very few questions are decidable.

sage: y = function('y')(x)
sage: desolve(diff(y,x,x) + x*diff(y,x) + y == 0, y, [0,0,1])
-1/2*I*sqrt(2)*sqrt(pi)*erf(1/2*I*sqrt(2)*x)*e^(-1/2*x^2)

We will go back to this in more detail in Chapter 14 and in §15.1.2.


2.3. ANALYSIS 27

2.3 Analysis
This section is a quick introduction of useful functions in real analysis. For more
advanced usage or more details, we refer to the following chapters, in particular
about numerical integration (Chapter 14), non-linear equations (Chapter 12), and
differential equations (Chapter 10).

2.3.1 Sums
The sum function computes symbolic sums. Let us obtain for example the sum of
the n first positive integers:
sage: k, n = var('k, n')
sage: sum(k, k, 1, n).factor()
1
2 (n + 1)n

The sum function allows simplifications of a binomial expansion:


sage: n, k, y = var('n, k, y')
sage: sum(binomial(n,k) * x^k * y^(n-k), k, 0, n)
n
(x + y)
Here are more examples, among them the sum of the cardinalities of all parts of
a set of n elements:
sage: k, n = var('k, n')
sage: sum(binomial(n,k), k, 0, n),\
....: sum(k * binomial(n, k), k, 0, n),\
....: sum((-1)^k*binomial(n,k), k, 0, n)

2n , 2n−1 n, 0
Finally, some examples of geometric sums:
sage: a, q, k, n = var('a, q, k, n')
sage: sum(a*q^k, k, 0, n)
aq n+1 −a
q−1

To compute the corresponding power series, we should tell Sage that the
modulus2 of q is less than 1:
sage: assume(abs(q) < 1)
sage: sum(a*q^k, k, 0, infinity)
a
− q−1

sage: forget(); assume(q > 1); sum(a*q^k, k, 0, infinity)


Traceback (most recent call last):
...
ValueError: Sum is divergent.
2 Remember that by default, symbolic variables represent complex values.
28 CHAP. 2. ANALYSIS AND ALGEBRA

Exercise 2 (Computing a sum by recurrence). Compute, without using the sum


command, the sum of p-powers of integers from 0 to n, for p = 1, ..., 4:
n
X
Sn (p) = kp .
k=0

The following recurrence can be useful to compute this sum:


p−1   !
1 p+1
X p+1
Sn (p) = (n + 1) − Sn (j) .
p+1 j
j=0

This recurrence
P is easily obtained when computing by two different methods the telescopic
sum (k + 1)p+1 − kp+1 .
0≤k≤n

2.3.2 Limits
To determine a limit, we use the limit command or its alias lim. Let us compute
the following limits:
√3
x−2
a) lim √ ;
x→8 3
x + 19 − 3

cos π4 − x − tan x
b) limπ  .
x→ 4 1 − sin π4 + x

sage: limit((x**(1/3) - 2) / ((x + 19)**(1/3) - 3), x = 8)


9/4
sage: f(x) = (cos(pi/4-x)-tan(x))/(1-sin(pi/4 + x))
sage: limit(f(x), x = pi/4)
Infinity
The last output says that one of the limits to the left or to the right is infinite.
To know more about this, we study the limits to the left (minus) and to the right
(plus), with the dir option:
sage: limit(f(x), x = pi/4, dir='minus')
+Infinity
sage: limit(f(x), x = pi/4, dir='plus')
-Infinity

2.3.3 Sequences
The above functions enable us to study sequences of numbers. We illustrate this
by comparing the growth of an exponential sequence and a geometric sequence.
100
n
Example. (A sequence study) Let us consider the sequence un = 100 n.

Compute the first 10 terms. How does the


 sequence
 vary? What is the sequence
limit? From which value of n does un ∈ 0, 10−8 hold?
2.3. ANALYSIS 29

1. To define the term of order n, we use a symbolic function. We then compute


the first 10 terms by hand (loops will be introduced in Chapter 3):
sage: u(n) = n^100 / 100^n
sage: u(1.);u(2.);u(3.);u(4.);u(5.);u(6.);u(7.);u(8.);u(9.);u(10.)
0.0100000000000000
1.26765060022823e26
5.15377520732011e41
1.60693804425899e52
7.88860905221012e59
6.53318623500071e65
3.23447650962476e70
2.03703597633449e74
2.65613988875875e77
1.00000000000000e80

We could quickly conclude that un tends to infinity...


2. To get an idea of the variation of the sequence, we can draw the graph of
the function n → un (cf. Figure 2.2).
sage: plot(u(x), x, 1, 40)
Graphics object consisting of 1 graphics primitive

1.6e90

1.4e90

1.2e90

1e90

8e89

6e89

4e89

2e89

0
5 10 15 20 25 30 35 40

Figure 2.2 – Graph of x 7→ x100 /100x .

We then conjecture that the sequence decreases from index 22 onwards.


sage: v(x) = diff(u(x), x); sol = solve(v(x) == 0, x); sol
[x == 50/log(10), x == 0]
sage: floor(sol[0].rhs())
21

The sequence is thus increasing up to index 21, then decreasing after index
22.
30 CHAP. 2. ANALYSIS AND ALGEBRA

Functions and operators


Derivative diff(f(x), x)
n-th derivative diff(f(x), x, n)
Antiderivative integrate(f(x), x)
Numerical integration integral_numerical(f(x), a, b)
Symbolic summation sum(f(i), i, imin, imax)
Limit limit(f(x), x=a)
Taylor expansion taylor(f(x), x, a, n)
Power series expansion f.series(x==a, n)
Graph of a function plot(f(x), x, a, b)

Table 2.4 – Useful functions in analysis.

3. We then compute the limit:


sage: limit(u(n), n=infinity)
0
sage: n0 = find_root(u(n) - 1e-8 == 0, 22, 1000); n0
105.07496210187252

Since the sequence decreases from index 22 onwards, we deduce


 that
 starting
from index 106, the sequence always lies in the interval 0, 10−8 .

2.3.4 Power Series Expansions (*)


To compute a power series expansion of order n at x0 , the command to use is
f(x).series(x==x0, n).
Let us determine the power series expansion of the following functions:
1
a) (1 + arctan x) x of order 3, at x0 = 0;
π
b) ln(2 sin x) of order 3, at x0 = 6.

sage: ((1+arctan(x))^(1/x)).series(x==0, 3)

(e) + (− 21 e)x + ( 18 e)x2 + O x3

sage: (ln(2*sin(x))).series(x==pi/6, 3)
√  
2 3
( 3)(− 61 π + x) + (−2)(− 61 π + x) + O − 216
1
(π − 6 x)

To extract the regular part of a power series expansion obtained by series,


we call the truncate method:
sage: (ln(2*sin(x))).series(x==pi/6, 3).truncate()
1 2 √
− 18 (π − 6 x) − 16 3(π − 6 x)
The taylor command provides asymptotic expansions too. For example, let
1 1
us see how the function (x3 + x) 3 − (x3 − x) 3 behaves around +∞:
2.3. ANALYSIS 31

sage: taylor((x**3+x)**(1/3) - (x**3-x)**(1/3), x, infinity, 2)


2/3/x
Exercise 3 (Computing a symbolic limit). Let f be C 3 around a ∈ R. Compute
1
lim (f (a + 3h) − 3f (a + 2h) + 3f (a + h) − f (a)) .
h→0 h3
Generalisation?
Example. (Machin’s formula) Prove the following formula:
π 1 1
= 4 arctan − arctan .
4 5 239
The astronomer John Machin (1680-1752) used this formula and the series expan-
sion of arctan to compute 100 decimal digits of π in 1706.
We first notice that 4 arctan 51 and π4 + arctan 239
1
admit the same tangent:
sage: tan(4*arctan(1/5)).simplify_trig()
120/119
sage: tan(pi/4+arctan(1/239)).simplify_trig()
120/119
Since the real numbers 4 arctan 51 and π4 + arctan 239
1
are both in the open
interval ]0, π[, they are equal. To obtain an approximation of π, we thus proceed
as follows:
sage: f = arctan(x).series(x, 10); f
1*x + (-1/3)*x^3 + 1/5*x^5 + (-1/7)*x^7 + 1/9*x^9 + Order(x^10)
sage: (16*f.subs(x==1/5) - 4*f.subs(x==1/239)).n(); pi.n()
3.14159268240440
3.14159265358979
Exercise 4 (A formula due to Gauss). The following formula required 20 pages of fac-
torisation tables in the edition of Gauss’ works (cf. Werke, ed. Königl. Ges. d. Wiss. Göt-
tingen, vol. 2, p. 477-502):
π 1 1 1 1
= 12 arctan + 20 arctan + 7 arctan + 24 arctan .
4 38 57 239 268
1 1 1 1
1. Define θ = 12 arctan 38 + 20 arctan 57
+ 7 arctan 239
+ 24 arctan 268
.
Verify with Sage that tan θ = 1.
2. Justify the inequality: ∀x ≥ 0, arctan x ≤ x. Deduce Gauss’ formula.
3. Approximate the arctan function by its Taylor expansion of order 21 at 0, and
deduce a new approximation of π.

2.3.5 Series (*)


The commands introduced earlier can be used to perform computations on series.
Let us give some examples.
Example. (Evaluation of the Riemann zeta function)
32 CHAP. 2. ANALYSIS AND ALGEBRA

sage: k = var('k')
sage: sum(1/k^2, k, 1, infinity),\
....: sum(1/k^4, k, 1, infinity),\
....: sum(1/k^5, k, 1, infinity)
1 2 1 4

6 π , 90 π , ζ(5)

Example. (A formula due to Ramanujan) Using the first 12 terms of the


following series, we give an approximation of π and we compare it with the value
given by Sage.
√ +∞
1 2 2 X (4k)! · (1103 + 26390 k)
= .
π 9801 (k!)4 · 3964k
k=0

sage: s = 2*sqrt(2)/9801*(sum((factorial(4*k)) * (1103+26390*k) /


....: ((factorial(k)) ^ 4 * 396 ^ (4 * k)) for k in (0..11)))
sage: (1/s).n(digits=100)
3.141592653589793238462643383279502884197169399375105820974...
sage: (pi-1/s).n(digits=100).n()
-4.36415445739398e-96

We notice that the partial sum of the first 12 terms already yields 95 significant
digits of π!
Example. (Convergence of a series) Let us study the convergence of the
series  p 
X
sin π 4 n2 + 1 .
n≥0

To get an asymptotic expansion of the general term, we use the 2π-periodicity of


the sine function, so that the sine argument tends to 0:
 p  h p i
un = sin π 4 n2 + 1 = sin π 4 n2 + 1 − 2n .

We can then apply the taylor function to this new expression of the general
term:

sage: n = var('n'); u = sin(pi*(sqrt(4*n^2+1)-2*n))


sage: taylor(u, n, infinity, 3)
π 6 π+π 3
4n − 384 n3

We deduce un ∼ 4πn . Therefore,P by comparison with the series defining the


Riemann zeta function, the series un diverges.
n≥0

Exercise 5 (Asymptotic expansion of a sequence). It is easy to show (for example,


using a bijection) that for all n ∈ N, the equation tan x = x has exactly one solution xn
in the interval [nπ, nπ + π2 [. Give an asymptotic expansion of xn to order 6 in +∞.
2.3. ANALYSIS 33

2.3.6 Derivatives
The derivative function (with alias diff) computes the derivative of a symbolic
expression or function.
sage: diff(sin(x^2), x)
2*x*cos(x^2)
sage: function('f')(x); function('g')(x); diff(f(g(x)), x)
f(x)
g(x)
D[0](f)(g(x))*diff(g(x), x)
sage: diff(ln(f(x)), x)
diff(f(x), x)/f(x)

2.3.7 Partial Derivatives (*)


The derivative (or diff) command also computes iterated or partial deriva-
tives.
sage: f(x,y) = x*y + sin(x^2) + e^(-x); derivative(f, x)
(x, y) |--> 2*x*cos(x^2) + y - e^(-x)
sage: derivative(f, y)
(x, y) |--> x
Example. Let us check that the following function is harmonic3 :
f (x, y) = 21 ln(x2 + y 2 ) for all (x, y) 6= (0, 0).
sage: x, y = var('x, y'); f = ln(x**2+y**2) / 2
sage: delta = diff(f,x,2) + diff(f,y,2)
sage: delta.simplify_rational()
0
Exercise 6 (A counter-example due to Peano to Schwarz’ theorem). Let f be the
function from R2 to R defined by:
( 2 2
xy xx2 −y
+y 2
if (x, y) 6= (0, 0),
f (x, y) =
0 if (x, y) = (0, 0).

Does ∂1 ∂2 f (0, 0) = ∂2 ∂1 f (0, 0) hold?

2.3.8 Integrals
To compute an indefinite or definite integral, we use integrate as a function or
method (or its alias integral):
sage: sin(x).integral(x, 0, pi/2)
1
sage: integrate(1/(1+x^2), x)
arctan(x)
3A function f is said harmonic when its Laplacian ∆f = ∂12 f + ∂22 f is zero.
34 CHAP. 2. ANALYSIS AND ALGEBRA

sage: integrate(1/(1+x^2), x, -infinity, infinity)


pi
sage: integrate(exp(-x**2), x, 0, infinity)
1/2*sqrt(pi)

sage: integrate(exp(-x), x, -infinity, infinity)


Traceback (most recent call last):
...
ValueError: Integral is divergent.
Z +∞
x cos u
Example. Let us compute, for x ∈ R, the integral ϕ(x) = du.
0 u2 + x2
sage: u = var('u'); f = x * cos(u) / (u^2 + x^2)
sage: assume(x>0); f.integrate(u, 0, infinity)
1/2*pi*e^(-x)
sage: forget(); assume(x<0); f.integrate(u, 0, infinity)
-1/2*pi*e^x
We thus have: ∀x ∈ R∗ , ϕ(x) = π2 · sgn(x) · e−|x| .
To compute numerically an integral on an interval, we have at our disposal
the integral_numerical function, which returns a pair, whose first value is
the approximation of the integral, while the second value is an estimate of the
corresponding error.
sage: integral_numerical(sin(x)/x, 0, 1)
(0.946083070367183, 1.0503632079297087e-14)
sage: g = integrate(exp(-x**2), x, 0, infinity)
sage: g, g.n()
(1/2*sqrt(pi), 0.886226925452758)
sage: approx = integral_numerical(exp(-x**2), 0, infinity)
sage: approx
(0.8862269254527568, 1.714774436012769e-08)
sage: approx[0]-g.n()
-1.11022302462516e-15
Exercise 7 (The BBP formula (*)). Let us establish by a symbolic computation
the BBP formula (or Bailey-Borwein-Plouffe formula); this formula directly gives the
n-th digit of π in radix 2 (or 16) without computing the previous digits, and with very
little memory usage and time. For N ∈ N, let us define
N   n
X 4 2 1 1 1
SN = − − − .
8n + 1 8n + 4 8n + 5 8n + 6 16
n=0
√ √
1. Consider the function f : t 7−→ 4 2 − 8t3 − 4 2t4 − 8t5 . For N ∈ N, express the
following integral as a function of SN :
Z √
N
!
1/ 2 X 8n
IN = f (t) t dt.
0 n=0
2.4. BASIC LINEAR ALGEBRA 35

Usual functions on vectors


Vector construction vector
Cross product cross_product
Scalar product dot_product
Norm of a vector norm

Table 2.5 – Vector computations.

Z √
1/ 2
f (t)
2. For N ∈ N, let us define J = dt. Prove lim SN = J.
0
1 − t8 N →+∞

3. Prove the BBP formula:


+∞ 
X  n
4 2 1 1 1
− − − = π.
8n + 1 8n + 4 8n + 5 8n + 6 16
n=0

This fabulous formula was found on September 19, 1995 by Simon Plouffe, in
collaboration with David Bailey and Peter Borwein. Thanks to computation
derived from the BBP formula, the 4 000 000 000 000 000-th digit of π in radix 2
was computed in 2001.

2.4 Basic Linear Algebra (*)


In this section, we describe the basic useful functions in linear algebra: first
operations on vectors, then on matrices. For more details, we refer the reader
to Chapter 8 for symbolic linear algebra, and to Chapter 13 for numerical linear
algebra.

2.4.1 Solving Linear Systems


To solve a linear system, we can use the solve function, already seen above.
Exercise 8 (Polynomial approximation of the sine function). Determine the poly-
nomial of degree at most 5 which approximates best, in the least squares sense, the sine
function on the interval [−π, π]:
Z π 
α5 = min |sin x − P (x)|2 dx P ∈ R5 [x] .
−π

2.4.2 Vector Computations


The basic functions for manipulating vectors are summarised in Table 2.5.
We can use those functions to deal with the following exercise.
Exercise 9 (Gauss’ problem). Consider a satellite in orbit around the Earth, and
assume we know three points of its orbit: A1 , A2 and A3 . Determine from these three
points the orbit parameters of this satellite.
Let us denote O the centre of the Earth. The points O, A1 , A2 and A3 are clearly
in the same plane, namely the plane defined by the satellite orbit. The satellite orbit is
36 CHAP. 2. ANALYSIS AND ALGEBRA

an ellipse of which O is a focal point. We can choose as coordinate system (O; → −ı , →


− )
p
in such a way that the ellipse equation in polar coordinates is r = 1−e cos θ where e is
−−→
r = OA and r = k→
the ellipse eccentricity, and p its parameter. We will note →

i

r k for i i i
i ∈ {1, 2, 3}. We then consider the three following vectors deduced from A1 , A2 and A3 :


D =→−
r1 ∧ →

r2 + → −
r2 ∧ →

r3 + →−
r3 ∧ →−
r1 ,


S = (r1 − r3 ) · r2 + (r3 − r2 ) · →

− −
r1 + (r2 − r1 ) · →

r3 ,

− →
− →
− →
− →
− →
− →

N = r · (r ∧ r ) + r · (r ∧ r ) + r · (r ∧ r ) .
3 1 2 1 2 3 2 3 1

−ı ∧ →
1. Show that →
− →

D = − 1e S and deduce the ellipse eccentricity.
−ı is colinear with the vector →
2. Show that →
− → −
S ∧ D.

− →

3. Show that →
−ı ∧ N = − p S and deduce the ellipse parameter p.
e

4. Compute the half major axis a of the ellipse in term of the parameter p and the
eccentricity e.
5. Numerical application: in the plane with a Cartesian coordinate system, we
consider the following points:

A1 ( 01 ) , A2 ( 22 ) , A3 ( 3.5
0 ), O ( 00 ) .

Determine numerically the characteristics of the unique ellipse having O as focal


point and passing through the three points A1 , A2 and A3 .

2.4.3 Matrix Computations


To construct a matrix, what you want is the matrix function, which allows to
optionally specify the base ring (or field):
sage: A = matrix(QQ, [[1,2],[3,4]]); A
[1 2]
[3 4]
To find a particular solution of the matrix equation Ax = b (resp. xA = b), we call
the solve_right function (resp. solve_left). To find all the solutions, we should
add to that particular solution the general solution of the associated homogeneous
equation. To solve a homogeneous equation Ax = 0 (resp. xA = 0), we use the
right_kernel (resp. left_kernel) function, as in the following exercise.
Exercise 10 (Basis of vector subspace).
1. Determine a basis of the space of solutions of the linear homogeneous system
corresponding to the matrix:
 
2 −3 2 −12 33
 6 1 26 −16 69 
A= .
10 −29 −18 −53 32 
2 0 8 −18 84

2. Determine a basis of the space F generated by the columns of A.


3. Characterise F by one or several equations.
2.4. BASIC LINEAR ALGEBRA 37

Usual functions on matrices


Construction of a matrix matrix
Solving a matrix equation solve_right, solve_left
Right and left kernel right_kernel, left_kernel
Row echelon form echelon_form
Column-generated vector space column_space
Row-generated vector space row_space
Matrix concatenation block_matrix

Matrix reduction
Eigenvalues of a matrix eigenvalues
Eigenvectors of a matrix eigenvectors_right
Jordan normal form reduction jordan_form
Minimal polynomial minimal_polynomial
Characteristic polynomial characteristic_polynomial

Table 2.6 – Matrix computations.

Exercise 11 (A matrix equation). Let us recall the factorisation lemma for linear
maps. Let E, F, G be K-vector spaces of finite dimension. Let u ∈ L(E, F ) and
v ∈ L(E, G). Then the following assertions are equivalent:
i) there exists w ∈ L(F, G) such that v = w ◦ u,
ii) Ker(u) ⊂ Ker(v).
We search all solutions to this problem in a concrete case. Let
 −2 1 1
  1 2 −1

A= 8 1 −5 and C= 2 −1 −1 .
4 3 −3 −5 0 3

Determine all solutions B ∈ M3 (R) of the equation A = BC.

2.4.4 Reduction of a Square Matrix


To study the eigenvalues and eigenvectors of a matrix, all functions from Table 2.6
are available. Those functions will be detailed in Chapter 8. We only give here a
few simple examples of their usage.
 2 4 3
Example. Is the matrix A = −4 −6 −3 diagonalisable? Triangularisable?
3 3 1
We start by defining the matrix A by giving the base field (QQ=Q), then we
determine its eigenvalues and eigenvectors.
sage: A = matrix(QQ, [[2,4,3],[-4,-6,-3],[3,3,1]])
sage: A.characteristic_polynomial()
x^3 + 3*x^2 - 4
sage: A.eigenvalues()
[1, -2, -2]
sage: A.minimal_polynomial().factor()
(x - 1) * (x + 2)^2
38 CHAP. 2. ANALYSIS AND ALGEBRA

The minimal polynomial of A admits a simple root and a double root; thus A
is not diagonalisable. However, its minimal polynomial being split into linear
factors, A is triangularisable.
sage: A.eigenvectors_right()
[(1, [(1, −1, 1)] , 1) , (−2, [(1, −1, 0)] , 2)]

sage: A.jordan_form(transformation=True)
   
1 0 0 1 1 1
 0 −2 1  ,  −1 −1 0 
0 0 −2 1 0 −1
 
1 −1/2
Example. Let us diagonalise the matrix A = −1/2 −1
. We first try the
jordan_form function:
sage: A = matrix(QQ, [[1,-1/2],[-1/2,-1]])
sage: A.jordan_form()
Traceback (most recent call last):
...
RuntimeError: Some eigenvalue does not exist in Rational Field.
A small difficulty appears here: the eigenvalues are not all rational.
sage: A = matrix(QQ, [[1,-1/2],[-1/2,-1]])
sage: A.minimal_polynomial()
x^2 - 5/4
We therefore have to change the base field.
sage: R = QQ[sqrt(5)]
sage: A = A.change_ring(R)
sage: A.jordan_form(transformation=True, subdivide=False)
 1  
sqrt 5 0  
 2 , 1 1 
1 −sqrt + 2 sqrt + 2
0 − sqrt 5 5 5
2
This is to be interpreted as:
 1 √   
2 5 √ 0 √ 1 √ 1
,
0 − 12 5 − 5+2 5+2
 √ √ 
√2 6 √2
Example. Let us diagonalise the matrix A = √6 √3 3 .
2 3 1
Here, we have to work in an extension of degree 4 of the field Q, for example
as follows.
sage: K.<sqrt2> = NumberField(x^2 - 2)
sage: L.<sqrt3> = K.extension(x^2 - 3)
sage: A = matrix(L, [[2, sqrt2*sqrt3, sqrt2], \
....: [sqrt2*sqrt3, 3, sqrt3], \
....: [sqrt2, sqrt3, 1]])
2.4. BASIC LINEAR ALGEBRA 39

sage: A.jordan_form(transformation=True)
   
6 0 0 √ √ 1 1 0
 0 0 0  ,  1 2 3 
2
1
√ √0 √1
0 0 0 2 2 − 2 − 3
40 CHAP. 2. ANALYSIS AND ALGEBRA
Programming and Data Structures
3
The two preceding chapters introduced mathematical computations using one-line
commands, but Sage also allows programs with sequences of instructions.
The Sage computer algebra system is in fact an extension of the Python1 com-
puter language, and allows, with a few exceptions, to use the Python programming
constructs.
The commands described in the previous chapters show that it is not necessary
to know the Python language to use Sage; this chapter explains how to use
the Python programming structures within Sage. Since we only present basic
programming, this chapter can be skipped by the reader fluent in Python; the
examples are chosen among the most classical ones encountered in mathematics,
so that the reader can quickly grasp the Python programming constructs, by
analogy with known programming languages.
This chapter presents in particular the paradigm of structured programming
with loops and tests, then describes functions dealing with lists and other data
structures.

3.1 Syntax
3.1.1 General Syntax
The instructions are generally processed line by line. Python considers the
sharp symbol “#” as the beginning of a comment, until the end of the line.
The semi-colon “;” separates several instructions written on the same line:

1 The Sage version considered here uses Python 2.7, which slightly differs from Python 3.
42 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

Python language keywords


while, for...in, if...elif...else loops and tests
continue, break early exit from a code block
try...except...finally, raise deal with and raise exceptions
assert debugging condition
pass no-effect statement
def, lambda definition of a function
return, yield return of a value
global, del scope and deleting variables and functions
and, not, or boolean operations
print text output
class, with object-oriented and context programming
from...import...as library access
exec...in dynamic code evaluation

Table 3.1 – General syntax of the Sagecode.

sage: 2*3; 3*4; 4*5 # one comment, 3 results


6
12
20

In the terminal, a command can be written on several lines by putting a backslash


“\” before each end of line, this character being ignored:
sage: 123 + \
....: 345
468

An identifier — i.e., a variable or function name, etc. — is formed from letters,


digits and the underline symbol “_”, and cannot start with a digit. The user
identifiers should differ from the language keywords, given in Table 3.1, and which
form the core of the Python language. The list of keywords is available by:
sage: import keyword; keyword.kwlist
['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del',
'elif', 'else', 'except', 'exec', 'finally', 'for', 'from',
'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass',
'print', 'raise', 'return', 'try', 'while', 'with', 'yield']

In addition to these keywords, we have the constants None (empty value, named
NULL in other languages), True and False, and several functions predefined by
Python or Sage like len, cos and integrate. It is better not to use these as
variable names, otherwise some functionalities might no longer be available. The
interpreter knows some additional commands, like quit to exit the Sage session.
We will discover other commands like time or timeit later in this book.
Some symbols have a special meaning in Sage. They are explained in Table 3.2.
3.1. SYNTAX 43

Sage special symbols and their main uses


,; argument and instruction separators
: beginning of an instruction block
. decimal point, accessing an object field
= assignment of a value to a variable
+ - * / basic arithmetic operations
^ ** power
// % quotient and remainder of Euclidean division
+= -= *= /= **= arithmetic operations with modification of a variable
== != <> is equality tests
< <= > >= comparisons
& | ^^ << >> set operations and bitwise logical operations
# comment (until end of line)
[...] construction of a list, accessing an element by its index
(...) function or method call, immutable tuples
{...:...} dictionary construction
\ special character escape (and linear algebra)
@ applying a decorator to a function
? ?? help and source code access
_ __ ___ last three results

Table 3.2 – General Sage syntax (following).

3.1.2 Function Calls


To evaluate a function, its arguments should be put inside parentheses — for
example cos(pi) — or in the function call without argument reset(). However,
the parentheses are superfluous for a command: the instructions print 6*7 and
print(6*7) are equivalent2 . The name of a function without argument nor
parenthesis represents the function itself and performs no computation.

3.1.3 More About Variables


As seen previously, Sage denotes the assignment of a value to a variable by the
equal sign “=”. The expression to the right of the equal sign is first evaluated,
then its value is saved in the variable whose name is on the left. Thus we have:
sage: y = 3; y = 3 * y + 1; y = 3 * y + 1; y
31
The three first assignments change the value of the variable y without any output,
the last command prints the final value of y.
The del x command discards the value assigned to the variable x, and the
function call reset() recovers the initial Sage state.
Several variables can be assigned simultaneously, which differs from sequential
assignments a = b; b = a:
sage: a, b = 10, 20 # (a, b) = (10, 20) and [10, 20] are also possible
2 In Python 3, print is a function and thus requires parentheses. This behaviour can be

obtained with from __future__ import print_function.


44 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

sage: a, b = b, a
sage: a, b
(20, 10)
The assignment a, b = b, a is equivalent to swapping the values of a and b
using an auxiliary variable:
sage: temp = a; a = b; b = temp # equivalent to: a, b = b, a
The following trick swaps the values of a and b without any auxiliary variable,
using additions and subtractions:
sage: x, y = var('x, y'); a = x ; b = y
sage: a, b
(x, y)
sage: a = a + b ; b = a - b ; a = a - b
sage: a, b
(y, x)
The instruction a = b = c = 0 assigns the same value, here 0, to several
variables; the instructions x += 5 and n *= 2 are respectively equivalent to x =
x+5 and n = n*2.
The comparison between two objects is performed by the double equal sign
“==”:
sage: 2 + 2 == 2^2, 3 * 3 == 3^3
(True, False)

3.2 Algorithmics
The paradigm of structured programming consists in designing a computer program
as a finite sequence of instructions, which are executed in order. Those instructions
can be atomic or composed:

• an example of atomic instruction is the assignment of a value to a variable


(cf. §1.2.4), or a result output;

• a composed instruction, like a loop or a conditional, is made up from several


instructions, themselves atomic or composed.

3.2.1 Loops
Enumeration Loops. An enumeration loop performs the same computation
for all integer values of an index k ∈ {a, . . . , b}: the following example3 outputs
the beginning of the multiplication table by 7:

3 When using Sage in a terminal, such a block of instructions must be ended by an additional

empty line, which will be implicit in the whole book. This is not necessary when using Sage
through a web browser.
3.2. ALGORITHMICS 45

sage: for k in [1..5]:


....: print(7*k) # block containing a single instruction
7
14
21
28
35
The colon symbol “:” at the end of the first line starts the instruction block,
which is evaluated for each successive value 1, 2, 3, 4 and 5 of the variable k. At
each iteration, Sage outputs the product 7k via the print command.
In this example, the repeated instruction block contains a single instruction
(namely print), which is indented to the right with respect to the for keyword.
A block with several instructions has its instructions written one below the other,
with the same indentation.
The block positioning is important: the two programs below, which differ in
the indentation of a single line, yield different results.

sage: S = 0 sage: S = 0
sage: for k in [1..3]: sage: for k in [1..3]:
... S = S+k ... S = S+k
sage: S = 2*S ... S = 2*S
sage: S sage: S

On the left the instruction S = 2*S is executed only once at the end of the loop,
while on the right it is executed at every iteration, which explains the different
results:

S = (0 + 1 + 2 + 3) · 2 = 12 S = ((((0 + 1) · 2) + 2) · 2 + 3) · 2 = 22.

This kind of loop will be useful to compute a given term of a recurrence, cf. the
examples at the end of this section.
The syntax for k in [a..b] for an enumeration loop is the simplest one
and can be used without any problem for 104 or 105 iterations; its drawback
is that it explicitly constructs the list of all possible values of the loop variable
before executing the iteration block, however it manipulates Sage integers of type
Integer (see §5.3.1). Several ..range functions allow iterations with two possible
choices. The first choice is: either construct the list of possible values before
starting the loop, or determine those values along with the loop iterations. The
second choice is between Sage integers4 (Integer) and Python integers (int),
those two integer types having slightly different properties. In case of doubt, the
[a..b] form should be preferred.

While Loops. The other kind of loops are the while loops. Like the enumeration
loops, they execute a certain number of times the same sequence of instructions;
4 The commands srange, sxrange and [...] also work on rational and floating-point numbers:

try [pi,pi+5..20] for example.


46 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

Iterations functions of the ..range form for a, b, c integers


for k in [a..b]: ... constructs the list of Sage integers a ≤ k ≤ b
for k in srange (a, b): ... constructs the list of Sage integers a ≤ k < b
for k in range (a, b): ... constructs a list of Python integers (int)
for k in xrange (a, b): ... enumerates Python integers (int) without
explicitly constructing the corresponding list
for k in sxrange (a, b): ... enumerates Sage integers without constructing a list
[a,a+c..b], [a..b, step=c] Sage integers a, a + c, a + 2c, . . . as long as a + kc ≤ b
..range (b) equivalent to ..range (0, b)
..range (a, b, c) sets the iteration increment to c instead of 1

Table 3.3 – The different enumeration loops.

however, here the number of repetitions is not known a priori, but depends on a
condition.
The while loop, as its name says, executes instructions while a given condition
is fulfilled. The following example computes the sum of the squares of non-negative
integers whose exponential is less or equal to 106 , i.e., 12 + 22 + · · · + 132 :
sage: S = 0 ; k = 0 # The sum S starts to 0
sage: while e^k <= 10^6: # e^13 <= 10^6 < e^14
....: S = S + k^2 # accumulates the squares k^2
....: k = k + 1
sage: S
819
The last instruction returns the value of the variable S and outputs the result:

X 13
X
S= k2 = k 2 = 819, e13 ≈ 442413 ≤ 106 < e14 ≈ 1202604.
k∈N k=0
ek ≤106

The above instruction block contains two assignments: the first one accumulates
the new term, and the second one moves to the next index. Those two instructions
are indented in the same way inside the while loop structure.
The following example is another typical example of while loop. For a given
number x ≥ 1, it seeks the unique integer n ∈ N satisfying 2n−1 ≤ x < 2n , i.e.,
the smallest integer with x < 2n . The program below compares x to 2n , whose
value is successively 1, 2, 4, 8, etc.; it performs this computation for x = 104 :
sage: x = 10^4; u = 1; n = 0 # invariant: u = 2^n
sage: while u <= x: n = n+1; u = 2*u # or n += 1; u *= 2
sage: n
14
As long as the condition 2n ≤ x is satisfied, this program computes the new values
n + 1 and 2n+1 = 2 · 2n of the two variables n and u, and stores them in place
of n and 2n . The loop ends when the condition is no longer fulfilled, i.e., when
3.2. ALGORITHMICS 47

x < 2n :

x = 104 , min{n ∈ N | x < 2n } = 14, 213 = 8192, 214 = 16384.

Note that the body of a while loop is never executed when the condition is false
at the first test.
As seen above, small command blocks can be typed on a single line after the
colon “:”, without creating a new indented block starting at the next line.

Aborting a loop execution

The for and while loops repeat a given number of times the same
instructions. The break command inside a loop interrupts it before its
end, and the continue command goes directly to the next iteration. Those
commands thus allow — among other things — to check the terminating
condition at every place in the loop.
The four examples below determine the smallest positive integer x satis-
fying log(x + 1) ≤ x/10. The first program (top left) uses a for loop with at
most 100 tries which terminates once the first solution is found; the second
program (top right) looks for the smallest solution and might not terminate
if the condition is never fulfilled; the third (bottom left) is equivalent to the
first one with a more complex loop condition; finally the fourth (bottom
right) has an unnecessarily complex structure, whose unique goal is to exhibit
the continue command. In all cases the final value x is 37.0.
for x in [1.0..100.0]: x=1.0
if log(x+1)<=x/10: break while log(x+1)>x/10:
x=x+1

x=1.0 x=1.0
while log(x+1)>x/10 and x<100: while True:
x=x+1 if log(x+1)>x/10:
x=x+1
continue
break

The return command (which ends the execution of a function and defines
its result, cf. §3.2.3) offers yet another way to abort early from an instruction
block.

Application to Sequences and Series. The for loop enables us to easily


compute a given term of a recurrent sequence. Consider for example the sequence
(un ) defined by
1
u0 = 1, ∀n ∈ N, un+1 = .
1 + u2n
The following program yields a numerical approximation of un for n = 20; the
variable U is updated at each loop iteration to change from un−1 to un according
48 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

to the recurrence relation. The first iteration computes u1 from u0 for n = 1, the
second one likewise from u1 to u2 when n = 2, and the last of the n iterations
updates U from un−1 to un :
sage: U = 1.0 # or U = 1. or U = 1.000
sage: for n in [1..20]:
....: U = 1 / (1 + U^2)
sage: U
0.682360434761105
The same program with the integer U = 1 instead of the floating-point number
U = 1.0 on the first line will perform exact computations on rational numbers;
then u10 becomes a rational number with several hundreds digits, and u20 has
hundreds of thousands digits. Exact computations are useful when rounding
errors accumulate in numerical approximations. Otherwise, by hand or with the
computer, the computations on numerical approximations of a dozen digits are
faster than those on integers or rational numbers of thousand digits or more.
The sums or products admitting recurrence formulas are computed the same
way:
Xn
Sn = (2k)(2k + 1) = 2 · 3 + 4 · 5 + · · · + (2n)(2n + 1),
k=1
S0 = 0, Sn = Sn−1 + (2n)(2n + 1) for n ∈ N − {0}.
The following programming method follows that of recurrent sequences; starting
from 0, we add successive terms for k = 1, k = 2, ..., until k = n:
sage: S = 0 ; n = 10
sage: for k in [1..n]:
....: S = S + (2*k) * (2*k+1)
sage: S
1650
This example highlights a general method to compute a sum; however, in this
simple case, a symbolic computation yields the general answer:
sage: n, k = var('n, k') ; res = sum(2*k*(2*k+1), k, 1, n)
sage: res, factor(res) # result expanded, factorised
(4/3*n^3 + 3*n^2 + 5/3*n, 1/3*(4*n + 5)*(n + 1)*n)
Those results might also be obtained with the pen and pencil method from
well-known sums:
n
X n
X
n(n + 1) n(n + 1)(2n + 1)
k= , k2 = ,
2 6
k=1 k=1
n
X n
X n
X 2 n (n + 1)(2n + 1)
2k (2k + 1) = 4 k2 + 2 k= + n(n + 1)
3
k=1 k=1 k=1

n(n + 1) (4n + 2) + 3 n(n + 1)(4n + 5)
= = .
3 3
3.2. ALGORITHMICS 49

Example: Approximation of Sequence Limits. While the enumeration


loop is well suited to compute a given term of a sequence or series, the while loop
is adapted to approximate numerically the limit of a sequence.
If a sequence (an )n∈N converges to ℓ ∈ R, the terms an are close to ℓ for n
large enough. It is thus possible to approximate ℓ by a given term an , and the
mathematical problem reduces to finding a bound for the error |ℓ − an |. This
bound is trivial for two sequences (un )n∈N and (vn )n∈N such that

 (un )n∈N is increasing,

(vn )n∈N is decreasing, (3.1)

 lim vn − un = 0.
n→+∞

In this case,

 the two sequences converge to the same limit ℓ,

∀p ∈ N up ≤ lim un = ℓ = lim vn ≤ vp ,
 n→+∞ n→+∞
 u +v v −u
ℓ − p2 p ≤ p 2 p .

A mathematical analysis shows


√ that the two following sequences satisfy the above
properties and converge to ab when 0 < a < b:
2 un vn un + vn
u0 = a, v0 = b > a, un+1 = , vn+1 = .
un + vn 2
The common limit of these two sequences is called arithmetic-harmonic mean
since the arithmetic mean of a and b is the average (a + b)/2, and the harmonic
mean is the inverse of the average inverse: 1/h = (1/a + 1/b)/2 = (a + b)/(2ab).
The following program checks the limit for given numerical values:
sage: U = 2.0; V = 50.0
sage: while V-U >= 1.0e-6: # 1.0e-6 stands for 1.0*10^-6
....: temp = U
....: U = 2 * U * V / (U + V)
....: V = (temp + V) / 2
sage: U, V
(9.99999999989256, 10.0000000001074)
The values un+1 and vn+1 depend on un and vn ; for this reason the main loop of
this program introduces an auxiliary variable temp to correctly compute the new
values un+1 , vn+1 of U, V from the previous values un , vn . The two left blocks
below define the same sequences, while the right one builds two other sequences:
temp = 2*U*V/(U+V) U,V = 2*U*V/(U+V),(U+V)/2 U = 2*U*V/(U+V)
V = (U+V)/2 V = (U+V)/2
2u′n vn

U = temp (parallel assignment) u′n+1 = u′ +v ′
n n
′ u′n+1 +vn

vn+1 = 2
Pn
The series Sn = k=0 (−1)k ak is alternating as soon as the sequence (ak )k∈N
is decreasing and tends to zero. Since S is alternating, the two subsequences
50 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

(S2n )n∈N and (S2n+1 )n∈N satisfy Eq. (3.1), with common limit say ℓ. Hence the
sequence (Sn )n∈N also converges to ℓ and we have S2p+1 ≤ ℓ = limn→+∞ Sn ≤
S2p .
The following program illustrates this result for the sequence ak = 1/k 3 from
k = 1, by storing in two variables U and V the partial sums S2n and S2n+1
enclosing the limit:
sage: U = 0.0 # the sum S0 is empty, of value zero
sage: V = -1.0 # S1 = -1/1^3
sage: n = 0 # U and V contain S(2n) and S(2n+1)
sage: while U-V >= 1.0e-6:
....: n = n+1 # n += 1 is equivalent
....: U = V + 1/(2*n)^3 # going from S(2n-1) to S(2n)
....: V = U - 1/(2*n+1)^3 # going from S(2n) to S(2n+1)
sage: V, U
(-0.901543155458595, -0.901542184868447)
The main loop increases the value of n until the two terms S2n and S2n+1 are
close enough. The two variables U and V contain two consecutive terms; the loop
body computes S2n from S2n−1 , and then S2n+1 from S2n , whence the crossed
assignments to U and V.
The program halts when two consecutive terms S2n+1 and S2n surrounding
the limit are close enough, the approximation error — without taking into account
rounding errors — satisfies then 0 ≤ a2n+1 = S2n − S2n+1 ≤ 10−6 .
Programming these five alternating series is similar:
X (−1)n X (−1)n X (−1)n
, , ,
log n n n2
n≥2 n≥1 n≥1
X (−1)n X X (−1)n
, (−1)n e−n ln n = .
n4 nn
n≥1 n≥1 n≥1

The terms of those series converge more or less rapidly to 0, thus the limit
approximations require more or fewer computations.
Looking for a precision of 3, 10, 20 or 100 digits on the limits of these series
consists in solving the following inequalities:
3
1/ log n ≤ 10−3 ⇐⇒ n ≥ e(10 ) ≈ 1.97 · 10434
1/n ≤ 10−3 103
⇐⇒ n ≥ √ 1/n ≤ 10−10 ⇐⇒ n ≥ 1010
1/n2 ≤ 10−3 ⇐⇒ n ≥ 103 ≈ 32 1/n2 ≤ 10−10 ⇐⇒ n ≥ 105
1/n4 ≤ 10−3 ⇐⇒ n ≥ (103 )1/4 ≈ 6 1/n4 ≤ 10−10 ⇐⇒ n ≥ 317
−n log n
e ≤ 10−3 ⇐⇒ n ≥ 5 e−n log n
≤ 10−10 ⇐⇒ n ≥ 10
1/n2 ≤ 10−20 ⇐⇒ n ≥ 1010 1/n2 ≤ 10−100 ⇐⇒ n ≥ 1050
1/n4 ≤ 10−20 ⇐⇒ n ≥ 105 1/n4 ≤ 10−100 ⇐⇒ n ≥ 1025
−n log n
e ≤ 10−20 ⇐⇒ n ≥ 17 e−n log n
≤ 10−100 ⇐⇒ n ≥ 57

In the simplest cases solving these inequalities yields an index n from which
the value Sn is close enough to the limit ℓ, and then a for enumeration loop is
3.2. ALGORITHMICS 51

possible. However, when it is not possible to solve the inequality an ≤ 10−p , a


while loop is necessary.
Numerical approximations of some of the above limits are too expensive, in
particular when the index n gets as large as 1010 or 1012 . A mathematical study
can sometimes determine the limit or approach it by other methods, like for the
series giving values of the Riemann zeta function:
n
X n
X
(−1)k 3 1
lim = − ζ(3), with ζ(p) = lim ,
n→+∞ k3 4 n→+∞ kp
k=1 k=1
Xn n
X
(−1)k (−1)k π2
lim = − log 2, lim =− ,
n→+∞ k n→+∞ k2 12
k=1 k=1
n
X k
(−1) 7π 4
lim = − .
n→+∞ k4 6!
k=1

Sage is able to compute symbolically some of these series, and determine a


1200-digit numerical approximation of ζ(3) in a few seconds, by doing far fewer
operations than the 10400 ones required by the definition:
sage: k = var('k'); sum((-1)^k/k, k, 1, +oo)
-log(2)
sage: sum((-1)^k/k^2, k, 1, +oo), sum((-1)^k/k^3, k, 1, +oo)
(-1/12*pi^2, -3/4*zeta(3))
sage: -3/4 * zeta (N(3, digits = 1200))
-0.901542677369695714049803621133587493073739719255374161344\
203666506378654339734817639841905207001443609649368346445539\
563868996999004962410332297627905925121090456337212020050039\
...
019995492652889297069804080151808335908153437310705359919271\
798970151406163560328524502424605060519774421390289145054538\
901961216359146837813916598064286672255343817703539760170306262

3.2.2 Conditionals
Another important instruction is the conditional (or test), which enables us to
execute some instructions depending on the result of a boolean condition. The
structure of the conditional and two possible syntaxes are:
if a condition: if a condition:
an instruction sequence an instruction sequence
else:
another instruction sequence
The Syracuse sequence is defined using a parity condition:
(
un /2 if un is even
u0 ∈ N − {0}, un+1 =
3un + 1 if un is odd.
52 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

The Collatz conjecture says — with no known proof in 2017 — that for all initial
values u0 ∈ N − {0}, there exists a rank n for which un = 1. The next terms are
then 4, 2, 1, 4, 2, etc. The computation of each term of this sequence requires a
parity test. This condition is checked within a while loop, which determines the
smallest n ∈ N satisfying un = 1:
sage: u = 6 ; n = 0
sage: while u != 1: # the test u <> 1 is also possible
....: if u % 2 == 0: # the operator % yields the remainder
....: u = u//2 # //: Euclidean division quotient
....: else:
....: u = 3*u+1
....: n = n+1
sage: n
8
Checking whether un is even is done by comparing to 0 the remainder of the
Euclidean division of un by 2. The variable n at the end of the block is the
number of iterations. The loop ends as soon as un = 1; for example if u0 = 6
then u8 = 1 and 8 = min{p ∈ N|up = 1}:

p = 0 1 2 3 4 5 6 7 8 9 10 ···
up = 6 3 10 5 16 8 4 2 1 4 2 ···

Verifying step-by-step the correct behaviour of the loop can be done using a
spy-instruction print(u, n) inside the loop body.
The if instruction also allows nested tests in the else branch using the elif
keyword. The two following structures are thus equivalent:

if a condition cond1: if cond1:


an instruction sequence inst1 inst1
else: elif cond2:
if a condition cond2: inst2
an instruction sequence inst2 elif cond3:
else: inst3
if a condition cond3: else:
an instruction sequence inst3 instn
else:
in other cases instn

Like for loops, small instruction sequences may be put after the colon on the
same line, rather than in an indented block below.

3.2.3 Procedures and Functions


General Syntax. As in other computer languages, the Sage user can define
her/his own procedures or functions, using the def command whose syntax is
detailed below. In this book, we call a function (resp. procedure) a sub-program
3.2. ALGORITHMICS 53

with zero, one or several arguments, which returns (resp. does not return) a result.
Let us define the function (x, y) 7→ x2 + y 2 :
sage: def fct2 (x, y):
....: return x^2 + y^2
sage: a = var('a')
sage: fct2 (a, 2*a)
5*a^2

The function evaluation ends with the return command, whose argument, here
x2 + y 2 , is the result of the function.
A procedure is like a function, but does not return any value, and without
any return instruction the instruction body of the procedure is evaluated until
its end. In fact a procedure returns the None value, which means “nothing”.
By default, all variables appearing in a function are considered local variables.
Local variables are created at each function call, destroyed at the end of the
function, and do not interact with other variables of the same name. In particular,
global variables are not modified by the evaluation of a function having local
variables of the same name:
sage: def foo (u):
....: t = u^2
....: return t*(t+1)
sage: t = 1 ; u = 2
sage: foo(3), t, u
(90, 1, 2)

It is possible to modify a global variable from within a function, with the global
keyword:
sage: a = b = 1
sage: def f(): global a; a = b = 2
sage: f(); a, b
(2, 1)

Consider again the computation of the arithmetic-harmonic mean of two


positive numbers:
sage: def AHmean (u, v):
....: u, v = min(u, v), max(u, v)
....: while v-u > 2.0e-8:
....: u, v = 2*u*v/(u+v), (u+v)/2
....: return (u+v) / 2

sage: AHmean (1., 2.)


1.41421356237309
sage: AHmean # corresponds to a function
<function AHmean at ...>
54 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

The AHmean function has two parameters u and v which are local variables, whose
initial values are those of the function arguments; for example with AHmean (1.,
2.) the function body begins with u = 1.0 and v = 2.0.
The structured programming paradigm recommends to have the return
statement at the very end of the function body. However, it is possible to put it
in the middle of the instruction block, then the following instructions will not be
executed. And the function body might contain several return occurrences.
Translating the mathematician’s viewpoint into the computer suggests the
use of functions that return results from their arguments, instead of procedures
that output those results with a print command. The Sage computer algebra
system is itself built on numerous functions like exp or solve, which return a
result, for example a number, an expression, a list of solutions, etc.

Iterative and Recursive Methods. As we have seen above, a user-defined


function is a sequence of instructions. A function is called recursive when during
its evaluation, it calls itself with different parameters. The factorial sequence is a
toy example of recursive sequence:

0! = 1, (n + 1)! = (n + 1) n! for all n ∈ N.

The two following functions yield the same result for a nonnegative integer
argument n; the first function uses the iterative method with a for loop, while
the second one is a word-by-word translation of the above recursive definition:
sage: def fact1 (n):
....: res = 1
....: for k in [1..n]: res = res*k
....: return res

sage: def fact2 (n):


....: if n == 0: return 1
....: else: return n*fact2(n-1)
The Fibonacci sequence is a recurrent relation of order 2 since un+2 depends on
un and un+1 :

u0 = 0, u1 = 1, un+2 = un+1 + un for all n ∈ N.

The function fib1 below applies an iterative scheme to compute terms of the
Fibonacci sequence: the variables U and V store the two previous values before
computing the next one:
sage: def fib1 (n):
....: if n == 0 or n == 1: return n
....: else:
....: U = 0 ; V = 1 # the initial terms u0 and u1
....: for k in [2..n]: W = U+V ; U = V ; V = W
....: return V
sage: fib1(8)
3.2. ALGORITHMICS 55

21

The for loop applies the relation un = un−1 + un−2 from n = 2. Note: a parallel
assignment U,V = V,U+V in place of W=U+V ; U=V ; V=W would avoid the need
of an auxiliary variable W, and would translate the order-1 vectorial recurrence
Xn+1 = f (Xn ) with f (a, b) = (b, a + b), for Xn = (un , un+1 ). Those iterative
methods are efficient, however programming them requires to manually deal with
variables corresponding to different terms of the sequence.
On the contrary, the recursive function fib2 follows more closely the mathe-
matical definition of the Fibonacci sequence, which makes its programming and
understanding easier:
sage: def fib2 (n):
....: if 0 <= n <= 1: return n # for n = 0 or n = 1
....: else: return fib2(n-1) + fib2(n-2)

The result of this function is the value returned by the conditional statement: either
0 or 1 respectively for n = 0 and n = 1, otherwise the sum fib2(n-1)+fib2(n-2);
each branch of the test consists of a return instruction.
This method is however less efficient since several computations are duplicated.
For example fib2(5) evaluates fib2(3) and fib2(4), which are in turn evaluated
in the same manner. Therefore, Sage computes twice fib2(3) and three times
fib2(2). This recursive process ends by the evaluation of either fib2(0) or
fib2(1), of value 0 or 1, and the evaluation of fib2(n) eventually consists in
computing un by adding un ones, and un−1 zeroes. The total number of additions
performed to compute un is thus un+1 − 1. This number grows very quickly, and
no computer is able to compute u100 this way.
Other methods are also possible, for example remembering the intermediate
terms using the decorator @cached_function, or using properties of matrix
powers: the following paragraph shows how to compute the millionth term of this
sequence. For example, compare the efficiency of the function fib2 defined above
with the following one, for example on n = 30:
sage: @cached_function
sage: def fib2a (n):
....: if 0 <= n <= 1: return n
....: else: return fib2a(n-1) + fib2a(n-2)

3.2.4 Example: Fast Exponentiation


The naive method for computing an for n ∈ N performs n multiplications by a
using a for loop:
sage: a = 2; n = 6; res = 1 # 1 is the product neutral element
sage: for k in [1..n]: res = res*a
sage: res # the value of res is 2^6
64
56 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

Integer powers often arise in mathematics and computer science; this paragraph
discusses a general method to compute an in a much faster way than the naive
method. The sequence (un )n∈N below satisfies un = an ; this follows by induction
2
from the equalities a2k = (ak ) and ak+1 = a ak :

1
 if n = 0,
2
un = un/2 if n is even positive, (3.2)


a un−1 if n is odd.

For example, for n = 11:

u11 = a u10 , u10 = u25 , u5 = a u4 , u4 = u22 ,


u2 = u21 , u1 = a u0 = a;

therefore:

u2 = a2 , u4 = u22 = a4 , u5 = a a4 = a5 ,
u10 = u25 = a10 , u11 = a a10 = a11 .

The computation of un only involves terms uk with k ∈ {0, ..., n − 1}, and is thus
well performed in a finite number of operations.
This example also shows that u11 is obtained after the evaluation of 6 terms u10 ,
u5 , u4 , u2 , u1 and u0 , which performs 6 multiplications only. In general, the
computation of un requires between log n/ log 2 and 2 log n/ log 2 multiplications.
Indeed, un is obtained from uk , k ≤ n/2, with one or two additional steps,
according to the parity of n. This method is thus much faster than the naive one
when n is large: about twenty products for n = 104 instead of 104 products:

indices k: 10 000 5 000 2 500 1 250 625 624 312 156 78


39 38 19 18 9 8 4 2 1

However, this method is not always the best one; the following operations using
b, c, d and f perform 5 products to compute a15 , whereas the above method —
using u, v, w, x and y — requires 6 products, without counting the initial product
a · 1:
b = a2 c = ab = a3 d = c2 = a6 f = cd = a9 df = a15 : 5 products;
u = a2 v = au = a3 w = v 2 = a6
x = aw = a7 y = x2 = a14 ay = a15 : 6 products.

The recursive function pow1 uses the recurrent sequence (3.2) to compute an :
sage: def pow1 (a, n):
....: if n == 0: return 1
....: elif n % 2 == 0: b = pow1 (a, n//2); return b*b
....: else: return a * pow1(a, n-1)

sage: pow1 (2, 11) # result is 2^11


3.2. ALGORITHMICS 57

2048

The number of operations performed by this function is the same as a computation


by hand using (3.2). In the case n is even, if the instructions b = pow1(a,
n//2);return b*b would be replaced by pow1(a, n//2)*pow1(a, n//2), Sage
would perform much more computations because, like for the recursive function
fib2 for the Fibonacci sequence, some calculations would be duplicated. We
would then have of the order of n products, i.e., as many as with the naive
method.
Note that instead of b = pow1(a, n//2);return b*b, we could write return
pow1(a*a, n//2).
The program below performs the same computation of an using an iterative
method:
sage: def pow2 (u, k):
....: v = 1
....: while k != 0:
....: if k % 2 == 0: u = u*u ; k = k//2
....: else: v = v*u ; k = k-1
....: return v

sage: pow2 (2, 10) # result is 2^10


1024

The fact that pow2(a, n) returns an is shown by verifying that after each iteration
the values of the variables u, v and k satisfy v uk = an , for whatever parity of k.
Before the first iteration v = 1, u = a and k = n; after the last one k = 0, thus
v = an .
The successive values of the integer variable k are nonnegative, and they form
a decreasing sequence. Hence this variable can only take a finite number of values
before being zero and terminating the loop.
Despite their apparent differences — pow1 is recursive, while pow2 is iterative
— those two functions express almost the same algorithm: the only difference is
2 k
that a2k is evaluated as (ak ) in pow1, and as (a2 ) in pow2, through the update
of the variable u.
The method presented here is not limited to the computation of an where
a is a number and n a positive integer, it applies to any associative law (which
is needed to preserve usual properties of iterated products). For instance, by
replacing the integer 1 by the m × m unit matrix 1m , the two above functions
would evaluate powers of square matrices. Those functions show how to efficiently
implement the power operator “ˆ” upon multiplication, and are similar to the
method implemented within Sage.
For example, using powers of matrices enables us to compute much larger
terms of the Fibonacci sequence:
   
0 1 un
A= , Xn = , AXn = Xn+1 , An X0 = Xn .
1 1 un+1
58 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

The corresponding Sage program fits in two lines, and the wanted result is the
first coordinate of the matrix product An X0 , which effectively works for n = 107 ;
the fib3 and fib4 programs are equivalent, and their efficiency comes from the
fact that Sage implements a fast exponentiation method:
sage: def fib3 (n):
....: A = matrix ([[0, 1], [1, 1]]) ; X0 = vector ([0, 1])
....: return (A^n*X0)[0]

sage: def fib4 (n):


....: return (matrix([[0,1], [1,1]])^n * vector([0,1]))[0]

3.2.5 Input and Output


The print instruction is the main output command. By default, its arguments
are printed one after the other, separated by spaces, with a newline after the
command:
sage: print 2^2, 3^3, 4^4 ; print 5^5, 6^6
4 27 256
3125 46656
A comma at the end tells the next print instruction to continue on the same
line:
sage: for k in [1..10]: print '+', k,
+ 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
To print results without intermediate spaces, we can transform them into a
character string using the str(..) function, and concatenate strings with the “+”
operator:
sage: print 10, 0.5 ; print 10+0.5 ; print 10.0, 5
10 0.500000000000000
10.5000000000000
10.0000000000000 5
sage: print 10+0, 5 ; print(str(10)+str(0.5))
10 5
100.500000000000000
The last section of this chapter discusses in more detail character strings.
The print command is also able to format the output: the following example
prints a table of fourth powers using the %.d placeholder and the % operator:
sage: for k in [1..6]: print('%2d^4 = %4d' % (k, k^4))
1^4 = 1
2^4 = 16
3^4 = 81
4^4 = 256
5^4 = 625
6^4 = 1296
3.3. LISTS AND OTHER DATA STRUCTURES 59

The % operator replaces the expressions to its right in the character string to its
left, in place of the placeholders like %2d or %.4f. In the above example the %4d
specifier adds some left padding spaces to the string representing k 4 , to get at
least four characters. Likewise, the %.4f placeholder in ’pi = %.4f’ % n(pi)
outputs pi = 3.1416 with four digits after the decimal point.
In a terminal, the raw_input(’message’) command prints the text message,
waits a keyboard input validated by the hEnteri key, and returns the user-given
character string.

3.3 Lists and Other Data Structures


This section discusses some data structures available in Sage: character strings,
lists — either mutable or immutable —, sets and dictionaries.

3.3.1 List Creation and Access


The list in computer science and the n-tuple in mathematics allow the enumeration
of mathematical objects. In a pair — with (a, b) 6= (b, a) — and an n-tuple, each
object has its own position, contrary to a set.
A list is defined by surrounding its elements with square brackets [...],
separated by commas. Assigning the triple (10, 20, 30) to the variable L is done
as follows, and the empty list is defined as:
sage: L = [10, 20, 30]
sage: L
[10, 20, 30]
sage: [] # [] is the empty list
[]
The list indices are increasing from 0, 1, 2, etc. The element of index k of a list
L is accessed simply by L[k], in mathematical terms this corresponds to the
canonical projection on the k-th coordinate. The number of elements of a list is
given by the len function5 :
sage: L[1], len(L), len([])
(20, 3, 0)
Modifying an element is done the same way, by simply assigning the corresponding
index. Hence the following command modifies the third term of the list, whose
index is 2:
sage: L[2] = 33
sage: L
[10, 20, 33]
Negative indices access end-of-list elements, L[-1] referring to the last one:

5 The output of len is a Python integer of type int, to get a Sage integer we write

Integer(len(..)).
60 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

sage: L = [11, 22, 33]


sage: L[-1], L[-2], L[-3]
(33, 22, 11)

The command L[p:q] extracts the sub-list [L[p], L[p+1], ..., L[q-1]],
which is empty if q ≤ p. Negative indices allow to reference the last terms of the
list; finally L[p:] is equivalent to L[p:len(L)], and L[:q] to L[0:q]:

sage: L = [0, 11, 22, 33, 44, 55]


sage: L[2:4]
[22, 33]
sage: L[-4:4]
[22, 33]
sage: L[2:-2]
[22, 33]
sage: L[:4]
[0, 11, 22, 33]
sage: L[4:]
[44, 55]

Similarly to the L[n] = ... command which modifies an element of the list, the
assignment L[p:q] = [...] substitutes all elements between index p included
and index q excluded:

sage: L = [0, 11, 22, 33, 44, 55, 66, 77]


sage: L[2:6] = [12, 13, 14] # substitutes [22, 33, 44, 55]

Therefore, L[:1] = [] and L[-1:] = [] delete respectively the first and last
term of a list, and likewise L[:0] = [a] and L[len(L):] = [a] insert the
element a respectively in front and in tail of the list. More generally the following
equalities hold:

L = [ℓ0 , ℓ1 , ℓ2 , . . . , ℓn−1 ] = [ℓ−n , ℓ1−n , . . . ℓ−2 , ℓ−1 ] with n = len(L),


ℓk = ℓk−n for 0 ≤ k < n, ℓj = ℓn+j for −n ≤ j < 0.

The operator in checks whether a list contains a given element, while “==”
compares two lists elementwise. The two sub-lists below with positive or negative
indices are equal:

sage: L = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]


sage: L[3:len(L)-5] == L[3-len(L):-5]
True
sage: [5 in L, 6 in L]
[True, False]

While we have considered so far lists with integer elements, list elements can be
any Sage object: numbers, expressions, other lists, etc.
3.3. LISTS AND OTHER DATA STRUCTURES 61

3.3.2 Global List Operations


The addition operator “+” concatenates two lists, and the multiplication operator
“*”, together with an integer, performs an iterated concatenation:
sage: L = [1, 2, 3] ; L + [10, 20, 30]
[1, 2, 3, 10, 20, 30]
sage: 4 * [1, 2, 3]
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
The concatenation of the two sub-lists L[:k] and L[k:] reconstructs the original
list. This explains why the left bound p of a sub-list L[p:q] is included, while
the right bound q is excluded:

L = L[:k]+L[k:] = [ℓ0 , ℓ1 , ℓ2 , . . . , ℓn−1 ]


= [ℓ0 , ℓ1 , ℓ2 , . . . , ℓk−1 ] + [ℓk , ℓk+1 , ℓk+2 , . . . , ℓn−1 ].

This property is shown in the following example:


sage: L = 5*[10, 20, 30] ; L[:3]+L[3:] == L
True
The operator made from two points “..” makes it easy to construct integer
lists without explicitly enumerating all elements, and can be mixed with isolated
elements:
sage: [1..3, 7, 10..13]
[1, 2, 3, 7, 10, 11, 12, 13]
We explain below how to build the image of a list under a function, and a
sub-list of a list. The corresponding functions are map and filter, together with
the [..for..x..in..] construction. Mathematics often involve lists made by
applying a function f to its elements:

(a0 , a1 , . . . , an−1 ) 7→ (f (a0 ), f (a1 ), . . . , f (an−1 )).

The map command builds this “map”: the following example applies the trigono-
metric function cos to a list of usual angles:
sage: map (cos, [0, pi/6, pi/4, pi/3, pi/2])
[1, 1/2*sqrt(3), 1/2*sqrt(2), 1/2, 0]
A user-defined function — with def — or a lambda-expression might also be used
as first argument of map; the following command is equivalent to the above, using
the function t 7→ cos t:
sage: map (lambda t: cos(t), [0, pi/6, pi/4, pi/3, pi/2])
[1, 1/2*sqrt(3), 1/2*sqrt(2), 1/2, 0]
The lambda command is followed by the parameters separated by commas,
and the colon must be followed by exactly one expression, which is the function
result (without the return keyword).
62 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

A lambda expression may contain a test, whence the following functions are
equivalent:

fctTest1 = lambda x: res1 if cond else res2


def fctTest2 (x):
if cond: return res1
else: return res2

As a consequence, the three following map commands are equivalent, the


composition N ◦ cos being expressed in different ways:
sage: map (lambda t: N(cos(t)), [0, pi/6, pi/4, pi/3, pi/2])
[1.00000000000000, 0.866025403784439, 0.707106781186548,
0.500000000000000, 0.000000000000000]

sage: map (N, map (cos, [0, pi/6, pi/4, pi/3, pi/2]))
[1.00000000000000, 0.866025403784439, 0.707106781186548,
0.500000000000000, 0.000000000000000]

sage: map (compose(N, cos), [0, pi/6, pi/4, pi/3, pi/2])


[1.00000000000000, 0.866025403784439, 0.707106781186548,
0.500000000000000, 0.000000000000000]
The filter command builds the sub-list of the elements satisfying a given
condition. To get all integers in 1, ..., 55 that are prime:
sage: filter (is_prime, [1..55])
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53]
The test condition might be defined inside the filter command, as in the
following example which finds by exhaustive search all fourth roots of 7 modulo
the prime 37; this equation has four solutions 3, 18, 19 and 34:
sage: p = 37 ; filter (lambda n: n^4 % p == 7, [0..p-1])
[3, 18, 19, 34]
Another way to build a list is using the comprehension form [..for..x..in..];
both commands below enumerate odd integers from 1 to 31:
sage: map(lambda n:2*n+1, [0..15])
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31]
sage: [2*n+1 for n in [0..15]]
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31]
The comprehension command is independent of the for loop. Associated with
the if condition, it yields an equivalent construction to filter:
sage: filter (is_prime, [1..55])
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53]
sage: [p for p in [1..55] if is_prime(p)]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53]
3.3. LISTS AND OTHER DATA STRUCTURES 63

In the two following examples, we combine the if and filter tests with the
comprehension for to determine a list of primes congruent to 1 modulo 4, then a
list of squares of prime numbers:
sage: filter (is_prime, [4*n+1 for n in [0..20]])
[5, 13, 17, 29, 37, 41, 53, 61, 73]
sage: [n^2 for n in [1..20] if is_prime(n)]
[4, 9, 25, 49, 121, 169, 289, 361]
In the first case the is_prime test is performed after the computation of 4n + 1,
while in the second one the primality test is done before the computation of the
square n2 .
The reduce function operates by associativity from left to right on the elements
of a list. Let us define the following operation, say ⋆:

x ⋆ y = 10x + y, then ((1 ⋆ 2) ⋆ 3) ⋆ 4 = (12 ⋆ 3) ⋆ 4 = 1234.

The first argument of reduce is a two-parameter function, the second one is the
list of its arguments:
sage: reduce (lambda x, y: 10*x+y, [1, 2, 3, 4])
1234
A third optional argument gives the image of an empty list:
sage: reduce (lambda x, y: 10*x+y, [9, 8, 7, 6], 1)
19876
This third argument usually corresponds to the neutral element of the operation
that is applied. The following example computes a product of odd integers:
sage: L = [2*n+1 for n in [0..9]]
sage: reduce (lambda x, y: x*y, L, 1)
654729075
The Sage functions add6 and prod apply directly the reduce operator to compute
sums and products; the three examples below yield the same result. The list
form enables us to add an optional second argument which stands for the neutral
element, 1 for the product and 0 for the sum, or a unit matrix for a matrix
product:
sage: prod ([2*n+1 for n in [0..9]], 1) # a list with for
654729075
sage: prod ( 2*n+1 for n in [0..9]) # without a list
654729075
sage: prod (n for n in [0..19] if n%2 == 1)
654729075
The function any associated to the or operator, and the function all to the and
operator, have similar syntax. Their evaluation terminates as soon as the result
True or False obtained for one term avoids the evaluation of the next terms:
6 Do not confuse add with sum, which looks for a symbolic expression of a sum.
64 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

sage: def fct (x): return 4/x == 2


sage: all (fct(x) for x in [2, 1, 0])
False
sage: any (fct(x) for x in [2, 1, 0])
True
In contrast, the construction of the list [fct(x) for x in [2, 1, 0]] and the
command all([fct(x) for x in [2, 1, 0]]) produce an error because all
terms are evaluated, including the last one with x = 0.
Nesting several for operators enables us to construct the cartesian product of
two lists, or to define lists of lists. As seen in the following example, the leftmost
for operator corresponds to the outermost loop:
sage: [[x, y] for x in [1..2] for y in [6..8]]
[[1, 6], [1, 7], [1, 8], [2, 6], [2, 7], [2, 8]]
The order therefore differs from that obtained by constructing a list of lists using
nested for comprehensions:
sage: [[[x, y] for x in [1..2]] for y in [6..8]]
[[[1, 6], [2, 6]], [[1, 7], [2, 7]], [[1, 8], [2, 8]]]
The map command with several lists as arguments takes one element of each
list in turn:
sage: map (lambda x, y: [x, y], [1..3], [6..8])
[[1, 6], [2, 7], [3, 8]]
Finally with the flatten command, we can concatenate lists on one or several
levels:
sage: L = [[1, 2, [3]], [4, [5, 6]], [7, [8, [9]]]]
sage: flatten (L, max_level = 1)
[1, 2, [3], 4, [5, 6], 7, [8, [9]]]
sage: flatten (L, max_level = 2)
[1, 2, 3, 4, 5, 6, 7, 8, [9]]
sage: flatten (L) # equivalent to flatten (L, max_level = 3)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
These elementary list operations are quite useful in other parts of Sage; the
following example computes the first successive derivatives of x ex ; the first
argument of diff is the expression to differentiate, and the following argument
is the derivation variable, or in the case of several arguments the variables with
respect to which the expression should be successively differentiated:
sage: x = var('x')
sage: factor(diff(x*exp(x), [x, x]))
(x + 2)*e^x
sage: map(lambda n: factor(diff(x*exp(x), n*[x])), [0..6])
[x*e^x, (x + 1)*e^x, (x + 2)*e^x, (x + 3)*e^x, (x + 4)*e^x,
(x + 5)*e^x, (x + 6)*e^x]
sage: [factor (diff (x*exp(x), n*[x])) for n in [0..6]]
3.3. LISTS AND OTHER DATA STRUCTURES 65

[x*e^x, (x + 1)*e^x, (x + 2)*e^x, (x + 3)*e^x, (x + 4)*e^x,


(x + 5)*e^x, (x + 6)*e^x]
The diff command admits more than one syntax. The parameters after the
function f can be a list of variables, an enumeration of variables, or a variable
and an order of derivation:
diff(f(x), x, x, x), diff(f(x), [x, x, x]), diff(f(x), x, 3).
We can also use diff(f(x), 3) for functions of one variable. The above results
are a direct consequence of Leibniz’ formula for iterated derivatives of a 2-term
product, given the fact that the derivatives of order 2 or more of x are zero:
Xn  
n (k) x (n−k)
(xex )(n) = x (e ) = (x + n)ex .
k
k=0

3.3.3 Main Methods on Lists


The reverse method reverts the order of elements in a list, and the sort method
transforms the given list into a sorted one:
sage: L = [1, 8, 5, 2, 9] ; L.reverse() ; L
[9, 2, 5, 8, 1]
sage: L.sort() ; L
[1, 2, 5, 8, 9]
sage: L.sort(reverse = True) ; L
[9, 8, 5, 2, 1]
Both methods modify the list L in-place, the initial list being lost.
A first optional argument of sort enables us to choose the order relation,
in form of a two-parameter function Order(x, y). The returned value of this
function must have the type int of the Python integers; it is negative, zero or
positive, for example −1, 0 or 1, when x ≺ y, x = y or x ≻ y, respectively. The
transformed list (x0 , x1 , . . . , xn−1 ) satisfies x0  x1  · · ·  xn−1 .
The lexicographic order of two number lists of same length is similar to the
alphabetic order and is defined as follows, ignoring the first equal terms:
P = (p0 , p1 , . . . pn−1 ) ≺lex Q = (q0 , q1 , . . . qn−1 )
⇐⇒ ∃r ∈ {0, . . . , n − 1} (p0 , p1 , . . . , pr−1 ) = (q0 , q1 , . . . , qr−1 ) and pr < qr .
The following function compares two lists of equal lengths. Despite the a priori
infinite loop while True, the return commands ensure the termination, together
with the finite length. The result is −1, 0 or 1 according to P ≺lex Q, P = Q or
P ≻lex Q:
sage: def alpha (P, Q): # len(P) = len(Q) by hypothesis
....: i = 0
....: while True:
....: if i == len(P): return int(0)
....: elif P[i] < Q[i]: return int(-1)
....: elif P[i] > Q[i]: return int(1)
....: else: i = i+1
66 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

sage: alpha ([2, 3, 4, 6, 5], [2, 3, 4, 5, 6])


1
The following command sorts a list of lists of same length using the lexicographic
order. The alpha function using the same order as used by Sage to compare two
lists, the command L.sort() without optional argument is thus equivalent:
sage: L = [[2, 2, 5], [2, 3, 4], [3, 2, 4], [3, 3, 3],\
....: [1, 1, 2], [1, 2, 7]]
sage: L.sort (cmp = alpha) ; L
[[1, 1, 2], [1, 2, 7], [2, 2, 5], [2, 3, 4], [3, 2, 4], [3, 3, 3]]
The homogeneous lexicographic order first compares terms according to their
weight, where the weight is the sum of coefficients, and only in the case of equal
weights resorts to the lexicographic order:

P = (p0 , p1 , . . . pn−1 ) ≺lexH Q = (q0 , q1 , . . . qn−1 )


n−1
X n−1
X  n−1
X n−1
X 
⇐⇒ pk < qk or pk = qk and P ≺lex Q .
k=0 k=0 k=0 k=0

This function implements the homogeneous lexicographic order:


sage: def homogLex (P, Q):
....: sp = sum (P) ; sq = sum (Q)
....: if sp < sq: return int(-1)
....: elif sp > sq: return int(1)
....: else: return alpha (P, Q)

sage: homogLex ([2, 3, 4, 6, 4], [2, 3, 4, 5, 6])


-1
The Sage function sorted is a function in the mathematical sense: it takes as
first argument a list and returns the corresponding sorted list, without modifying
its argument, unlike sort.
Sage provides other methods on lists, to insert an element at the tail, to
append a list at the end, to count the number of occurrences of an element:

L.append(x) is equivalent to L[len(L):] = [x]


L.extend(L1) is equivalent to L[len(L):] = L1
L.insert(i, x) is equivalent to L[i:i] = [x]
L.count(x) is equivalent to len ([t for t in L if t == x])

The commands L.pop(i) and L.pop() remove the element of index i, or the
last one, and return the removed element; their behaviour is described by these
two functions:
def pop1 (L, i): def pop2 (L):
a = L[i] return pop1 (L, len(L)-1)
L[i:i+1] = []
return a
3.3. LISTS AND OTHER DATA STRUCTURES 67

In addition, L.index(x) returns the index of the first element equal to x, and
L.remove(x) removes the first element equal to x. These commands raise an
error when x is not in the list. Finally, the command del L[p:q] is equivalent
to L[p:q] = [], and del L[i] removes the ith element.
Contrary to what happens in several other computer languages, these functions
modify in-place the list L, without creating a new list.

3.3.4 Examples of List Manipulations


The following example constructs the list of even terms and the list of odd terms
of a given list. This first solution goes twice through the list, and thus performs
the parity tests twice:
sage: def fct1(L):
....: return [filter (lambda n: n % 2 == 0, L),
....: filter (lambda n: n % 2 == 1, L)]

sage: fct1([1..10])
[[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
The second function below goes only once through the list, and constructs the
two result lists element by element:
sage: def fct2 (L):
....: res0 = [] ; res1 = []
....: for k in L:
....: if k%2 == 0: res0.append(k) # or res0[len(res0):] = [k]
....: else: res1.append(k) # or res1[len(res1):] = [k]
....: return [res0, res1]
This program replaces the for loop and the auxiliary variables by a recursive call
and an additional parameter:
sage: def fct3a (L, res0, res1):
....: if L == []: return [res0, res1]
....: elif L[0]%2 == 0: return fct3a(L[1:], res0+[L[0]], res1)
....: else: return fct3a (L[1:], res0, res1+[L[0]])

sage: def fct3 (L): return fct3a (L, [], [])


The parameters res0 and res1 contain the first element already treated, and the
parameter list L has one term less at each recursive call.
The second example below extracts all maximal non-decreasing sequences of
a list of numbers. Three variables are used, the first one res keeps track of all
non-decreasing sequences already obtained, the start variable is the starting
index of the current sub-sequence, and k is the loop index:
sage: def subSequences (L):
....: if L == []: return []
....: res = [] ; start = 0 ; k = 1
68 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

....: while k < len(L): # 2 consecutive terms are defined


....: if L[k-1] > L[k]:
....: res.append (L[start:k]) ; start = k
....: k = k+1
....: res.append (L[start:k])
....: return res

sage: subSequences([1, 4, 1, 5])


[[1, 4], [1, 5]]
sage: subSequences([4, 1, 5, 1])
[[4], [1, 5], [1]]
The loop body deals with the kth element of the list. If the condition is fulfilled,
the current non-decreasing sub-sequence ends, and we start a new sub-sequence,
otherwise the current sub-sequence is extended by one term.
After the loop body, the append instruction adds to the final result the current
sub-sequence, which contains at least one element.

3.3.5 Character Strings


Character strings are delimited by single or double quotes, ’...’ or "...".
Strings delimited by single quotes may contain double quotes, and vice versa.
Strings can also be delimited by triple quotes ’’’...’’’: in that case they may
span several lines and contain single or double quotes.
sage: S = 'This is a character string.'
The escape character is the \ symbol, which allows to include end of lines by \n,
quotes by \" or \’, tabulations by \t, the backslash character by \\. Character
strings may contain characters with accents, and more generally any Unicode
character:
sage: S = 'This is a déjà-vu example.'; S
'This is a d\xc3\xa9j\xc3\xa0-vu example.'
sage: print(S)
This is a déjà-vu example.
The comparison of two character strings is performed according to the internal
encoding of each character. The length of a string is given by the len function,
and the concatenation of strings is performed by the addition and multiplication
symbols “+” and “*”.
Accessing sub-strings of S is done as for lists using square brackets S[n],
S[p:q], S[p:] and S[:q], the result being a character string. The language
forbids the replacement of an initial string by such an assignment, for this reason
character strings are immutable.
The str function converts its argument into a character string. The split
method cuts a given string at spaces:
sage: S='one two three four five six seven'; L=S.split(); L
['one', 'two', 'three', 'four', 'five', 'six', 'seven']
3.3. LISTS AND OTHER DATA STRUCTURES 69

The very extensive Python library re may also be used to search sub-strings,
words and regular expressions.

3.3.6 Shared or Duplicated Data Structures


A list in square brackets [...] can be modified by assigning some of its elements,
by a change of the number of elements, or by methods like sort or reverse.
Assigning a list to a variable does not duplicate the data structure, which
is shared. In the following example the lists L1 and L2 remain identical: they
correspond to two aliases of the same object, and modifying one of them is visible
on the other one:
sage: L1 = [11, 22, 33] ; L2 = L1
sage: L1[1] = 222 ; L2.sort() ; L1, L2
([11, 33, 222], [11, 33, 222])
sage: L1[2:3] = []; L2[0:0] = [6, 7, 8]
sage: L1, L2
([6, 7, 8, 11, 33], [6, 7, 8, 11, 33])
In contrast, the map, filter and flatten functions duplicate the data structures;
so do the list construction by L[p:q] or [..for..if..], and the concatenation
by + and *.
In the above example, replacing on the first line L2 = L1 by one of the next six
commands completely changes the following results, since modifications on one list
do not propagate to the other one. The two structures become independent, the
two lists are distinct even if they have the same value; for example the assignment
L2 = L1[:] copies the sub-list of L1 from the first to last term, and thus fully
duplicates the structure of L1:

L2 = [11, 22, 33] L2 = copy(L1) L2 = L1[:]


L2 = []+L1 L2 = L1+[] L2 = 1*L1

Checking for shared data structures can be done in Sage using the is binary
operator; if the answer is true, all modifications will have a side effect on both
variables:
sage: L1 = [11, 22, 33] ; L2 = L1 ; L3 = L1[:]
sage: [L1 is L2, L2 is L1, L1 is L3, L1 == L3]
[True, True, False, True]
Copy operations on lists operate on one level only. As a consequence, modifying
an element in a list of lists has a side effect despite the list copy at the outer
level:
sage: La = [1, 2, 3] ; L1 = [1, La] ; L2 = copy(L1)
sage: L1[1][0] = 5 # [1, [5, 2, 3]] for L1 and L2
sage: [L1 == L2, L1 is L2, L1[1] is L2[1]]
[True, False, True]
The following instruction duplicates a list on two levels:
70 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

sage: map (copy, L)


whereas the deepcopy function recursively duplicates Python objects at all levels:
sage: La = [1, 2, 3] ; L1 = [1, La] ; L2 = deepcopy(L1)
sage: L1[1][0] = 5; [L1 == L2, L1 is L2, L1[1] is L2[1]]
[False, False, False]
The inverse lexicographic order is defined from the lexicographic order on
n-tuples by reversing the order on each element:
P = (p0 , p1 , . . . pn−1 ) ≺lexInv Q = (q0 , q1 , . . . qn−1 )
⇐⇒ ∃r ∈ {0, . . . , n − 1}, (pr+1 , . . . , pn−1 ) = (qr+1 , . . . , qn−1 ) and pr > qr .
Programming this inverse lexicographic order might be done using the above-
defined alpha function, which implements the lexicographic order. We have to
copy the lists P and Q to perform the inversion without modifying the lists. More
precisely the lexInverse function reverts the n-tuples by reverse, and returns
the opposite of the Python integer corresponding to the wanted comparison:
−(P1 ≺lex Q1 ):
sage: def lexInverse (P, Q):
....: P1 = copy(P) ; P1.reverse()
....: Q1 = copy(Q) ; Q1.reverse()
....: return - alpha (P1, Q1)
The changes made on a list given as argument of a function are performed on
the original list, since the functions do not copy arguments which are lists. Thus
a function that would perform P.reverse(), in place of P1 = copy(P) and
P1.reverse(), would modify definitively the list P ; this side effect is usually not
wanted.
The variable P is a local variable of the function, independent from any other
global variable also called P , but this has nothing to do with modifications made
to a list given as argument of the function.
The lists in Python and Sage are implemented as dynamic tables, contrary
to Lisp and OCaml where lists are defined by a head t and a tail list Q. The
Lisp command cons(t,Q) returns a list with head t without modifying the list Q,
whereas in Python, adding an element e to a dynamic table T via T.append(e)
modifies the table T . Both representations have advantages and drawbacks, and
switching from one to the other is possible, however the efficiency of a given
algorithm might greatly vary from one representation to the other.

3.3.7 Mutable and Immutable Data Structures


Lists enable us to construct and manipulate elements that can be modified: they
are called mutable data structures.
Python also allows to define immutable objects. The immutable data structure
corresponding to lists is called sequence or tuple, and is denoted with parentheses
(...) instead of square brackets [...]. A tuple with only one element is defined
by adding a comma after this element, to distinguish it from mathematical
parentheses.
3.3. LISTS AND OTHER DATA STRUCTURES 71

sage: S0 = (); S1 = (1, ); S2 = (1, 2)


sage: [1 in S1, 1 == (1)]
[True, True]
The operations on tuples are essentially the same as those on lists, for example
map constructs the image of a tuple by a function, filter extracts a sub-sequence.
In all cases the result is a list, and the for comprehension transforms a tuple in
list:
sage: S1 = (1, 4, 9, 16, 25); [k for k in S1]
[1, 4, 9, 16, 25]
The zip command groups several lists or tuples term-by-term, and is equivalent
to the following map command:
sage: L1 = [0..4]; L2 = [5..9]
sage: zip(L1, L2)
[(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)]
sage: map(lambda x, y:(x, y), L1, L2)
[(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)]

3.3.8 Finite Sets


Contrary to lists, the set data structure only keeps track of whether an element
is present or absent, without considering its position or number of repetitions.
Sage constructs finite sets via the Set function, applied to the list of its elements.
The result is output with curly brackets:
sage: E = Set([1, 2, 4, 8, 2, 2, 2]); F = Set([7, 5, 3, 1]); E, F
({8, 1, 2, 4}, {1, 3, 5, 7})
The operator in checks whether a set contains a given element, and Sage allows
the union of sets by + or |, the intersection by &, the set difference by -, and the
symmetric difference using ˆˆ:
sage: E = Set([1, 2, 4, 8, 2, 2, 2]); F = Set([7, 5, 3, 1])
sage: 5 in E, 5 in F, E + F == F | E
(False, True, True)
sage: E & F, E - F, E ^^ F
({1}, {8, 2, 4}, {2, 3, 4, 5, 7, 8})
The len(E) command gives the cardinality of such a finite set. The operations
map, filter and for..if... apply to sets as well as tuples, and yield lists
as results. Accessing a given element is done via E[k]. The commands below
construct in two different ways the list of elements of a set:
sage: E = Set([1, 2, 4, 8, 2, 2, 2])
sage: [E[k] for k in [0..len(E)-1]], [t for t in E]
([8, 1, 2, 4], [8, 1, 2, 4])
The following function checks whether E is a subset of F , using the union
operator:
72 CHAP. 3. PROGRAMMING AND DATA STRUCTURES

sage: def included (E, F): return E+F == F


Contrary to lists, sets are immutable, and thus cannot be modified; their
elements must also be immutable. Sets of tuples or sets of sets are thus possible,
but not sets of lists:
sage: Set([Set([]), Set([1]), Set([2]), Set([1, 2])])
{{1, 2}, {}, {2}, {1}}
sage: Set([ (), (1, ), (2, ), (1, 2) ])
{(1, 2), (2,), (), (1,)}
The following function scans all subsets of a set recursively:
sage: def Parts (EE):
....: if EE == Set([]): return Set([EE])
....: else:
....: return withOrWithout (EE[0], Parts(Set(EE[1:])))

sage: def withOrWithout (a, E):


....: return Set (map (lambda F: Set([a])+F, E)) + E

sage: Parts(Set([1, 2, 3]))


{{3}, {1, 2}, {}, {2, 3}, {1}, {1, 3}, {1, 2, 3}, {2}}
The withOrWithout(a, E) function call takes a set E of subsets, and constructs
the set twice as large made from those subsets, and those subsets added (in the
set union sense) with a. The recursive construction starts with a set with one
element E = {∅}.

3.3.9 Dictionaries
Last but not least, Python, and thus Sage, provides the notion of dictionary. Like
a phone book, a dictionary associates a value to a given key.
The keys of a dictionary might be of any immutable type: numbers, characters
strings, tuples, etc. The syntax is like lists, using assignments from the empty
dictionary dict() which can be written {} too:
sage: D={}; D['one']=1; D['two']=2; D['three']=3; D['ten']=10
sage: D['two'] + D['three']
5
The above example shows how to add an entry (key,value) to a dictionary, and
how to access the value associated to a given key via D[...].
The operator in checks whether a key is in a dictionary, and the commands
del D[x] or D.pop(x) erase the entry of key x in this dictionary.
The following example demonstrates how a dictionary can be used to represent
a function on a finite set:
E = {a0 , a1 , a2 , a3 , a4 , a5 }, f (a0 ) = b0 , f (a1 ) = b1 , f (a2 ) = b2 ,
f (a3 ) = b0 , f (a4 ) = b3 , f (a5 ) = b3 .
3.3. LISTS AND OTHER DATA STRUCTURES 73

Methods on dictionaries are comparable to those on other enumerated data


structures. The program below implements the above function, and gives the
input set E and the output set Im f = f (E) via the methods keys and values:
sage: D = {'a0':'b0', 'a1':'b1', 'a2':'b2', 'a3':'b0',\
....: 'a4':'b3', 'a5':'b3'}
sage: E = Set(D.keys()) ; Imf = Set(D.values())
sage: Imf == Set(map (lambda t:D[t], E)) # is equivalent
True
This last command directly translates the mathematical definition Im f =
{f (x)|x ∈ E}. Dictionaries may also be constructed from lists or pairs [key,
value] via the following command:

dict([’a0’, ’b0’], [’a1’, ’b1’], ...)

The two following commands, applied to the keys or to the dictionary itself
are, by construction, equivalent to D.values():

map (lambda t:D[t], D) map (lambda t:D[t], D.keys())

The following test on the number of distinct values determines if the function
represented by D is injective, len(D) being the number of dictionary entries:
sage: def injective(D):
....: return len(D) == len (Set(D.values()))
The first two commands below build the image f (F ) and the preimage f −1 (G)
of subsets F and G of a function defined by the dictionary D; the last one
constructs the dictionary DR corresponding to the inverse function f −1 of f ,
assumed to be bijective:
sage: Set([D[t] for t in F])
sage: Set([t for t in D if D[t] in G])
sage: DR = dict((D[t], t) for t in D)
74 CHAP. 3. PROGRAMMING AND DATA STRUCTURES
4
Graphics

Drawing a function of one or two variables, or a series of data, makes it easier to


grasp a mathematical or physical phenomenon, and helps us make conjectures.
In this chapter, we illustrate the graphical capabilities of Sage using several
examples.

4.1 2D Graphics
Several definitions of a plane curve are possible: as the graph of a function, from
a parametric system, using polar coordinates, or by an implicit equation. We
detail these four cases, and give some examples of data visualisation.

4.1.1 Graphical Representation of a Function


To draw the graph of a symbolic or Python function on an interval [a, b], we use
plot(f(x), a, b) or the alternative syntax plot(f(x), x, a, b).
sage: plot(x * sin(1/x), x, -2, 2, plot_points=500)
Among the numerous options of the plot command, we mention the following:
• plot_points (default value 200): minimal number of computed points;
• xmin and xmax: interval bounds over which the function is displayed;
• color: colour of the graph, either a RGB triple, a character string such as
’blue’, or an HTML colour like ’#aaff0b’;
• detect_poles (default value False): enables to draw a vertical asymptote
at poles of the function;
76 CHAP. 4. GRAPHICS

0.8

0.6

0.4

0.2

-2 -1.5 -1 -0.5 0.5 1 1.5 2

-0.2

1
Figure 4.1 – Graph of x 7→ x sin x
.

• alpha: line transparency;

• thickness: line thickness;

• linestyle: style of the line, either dotted with ’:’, dash-dotted with ’-.’,
or solid with the default value ’-’.

To visualise the graph, we assign the graphical object to a variable, say g,


then we use the show command; in addition we can give bounds for the y-axis
(g.show(ymin=-1, ymax=3)) or choose the aspect ratio (g.show(aspect_ratio=1)
to have equal scales for x and y).
The graph obtained may be exported using the save command into sev-
eral formats defined by the suffixes .pdf, .png, .ps, .eps, .svg and .sobj:
g.save(name, aspect_ratio=1, xmin=-1, xmax=3, ymin=-1, ymax=3)

To include such a figure in a LATEX document using the includegraphics


command, one should use the eps suffix (encapsulated PostScript) if the document
is to be compiled with latex, and the pdf suffix (to be preferred to png, to obtain
a better resolution) if the document is to be compiled with pdflatex.
Let us draw on the same graphics the sine function and its first Taylor
polynomials at 0.
sage: def p(x, n):
....: return(taylor(sin(x), x, 0, n))
sage: xmax = 15 ; n = 15
sage: g = plot(sin(x), x, -xmax, xmax)
sage: for d in range(n):
....: g += plot(p(x, 2 * d + 1), x, -xmax, xmax,\
....: color=(1.7*d/(2*n), 1.5*d/(2*n), 1-3*d/(4*n)))
sage: g.show(ymin=-2, ymax=2)
We can also create an animation, to see how the Taylor polynomials approximate
better and better the sine function when their degree increases. To keep the
animation, it suffices to save it in the gif format.
4.1. 2D GRAPHICS 77

2 4

1.5 3

1 2

0.5 1

-10 -5 5 10 -20 -10 10 20

-0.5 -1

-1 -2

-1.5 -3

-2 -4

Figure 4.2 – Some Taylor polynomials of the sine function at 0.

sage: a = animate([[sin(x), taylor(sin(x), x, 0, 2*k+1)]\


....: for k in range(0, 14)], xmin=-14, xmax=14,\
....: ymin=-3, ymax=3, figsize=[8, 4])
sage: a.show(); a.save('path/animation.gif')
Let us return to the plot function to demonstrate, as an example, the Gibbs
phenomenon. We draw the partial sum of order 20 of the square wave function.
sage: f2(x) = 1; f1(x) = -1
sage: f = piecewise([[(-pi,0),f1],[(0,pi),f2]])
sage: S = f.fourier_series_partial_sum(20,pi)
sage: g = plot(S, x, -8, 8, color='blue')
sage: saw(x) = x - 2 * pi * floor((x + pi) / (2 * pi))
sage: g += plot(saw(x) / abs(saw(x)), x, -8, 8, color='red')
sage: g

0.5

-8 -6 -4 -2 2 4 6 8

-0.5

-1

Figure 4.3 – Fourier series expansion of the square wave function.

In the code above, f is a piecewise function on [−π; π], defined with the piecewise
instruction. To extend f by 2π-periodicity, the simplest solution is to give an
expression valid for any real number, such as saw(x)/abs(saw(x)). The sum of
the 20 first terms of the Fourier series is:
 
4 sin(3x) sin(5x) sin(19π)
S= sin(x) + + + ··· + .
π 3 5 19
78 CHAP. 4. GRAPHICS

4.1.2 Parametric Curve


Parametric curves (x = f (t), y = g(t)) may be visualised using the command
parametric_plot((f(t), g(t)), (t, a, b)), where [a, b] is the interval over
which the parameter t ranges.
Let us show the parametric curve defined by the equations:

 1 1
x(t) = cos(t) + cos(7t) + sin(17t),
2 3
 y(t) = sin(t) + 1 sin(7t) + 1 cos(17t).

2 3

sage: t = var('t')
sage: x = cos(t) + cos(7*t)/2 + sin(17*t)/3
sage: y = sin(t) + sin(7*t)/2 + cos(17*t)/3
sage: g = parametric_plot((x, y), (t, 0, 2*pi))
sage: g.show(aspect_ratio=1)

1.5

0.5

-1.5 -1 -0.5 0.5 1 1.5

-0.5

-1

-1.5

1 1
Figure 4.4 – Parametric curve of equation x(t) = cos(t) + 2
cos(7t) + 3
sin(17t), y(t) =
sin(t) + 21 sin(7t) + 13 cos(17t).

4.1.3 Curve in Polar Coordinates


Curves in polar coordinates ρ = f (θ), where the parameter θ spans the interval
[a, b], may be drawn by the command polar_plot(rho(theta),(theta,a,b)).
For example, let us see graphically the rose-curves with polar equation ρ(θ) =
1 + e · cos nθ when n = 20/19 and e ∈ {2, 1/3}.
sage: t = var('t'); n = 20/19
sage: g1 = polar_plot(1+2*cos(n*t),(t,0,n*36*pi),plot_points=5000)
sage: g2 = polar_plot(1+1/3*cos(n*t),(t,0,n*36*pi),plot_points=5000)
4.1. 2D GRAPHICS 79

1
2

0.5
1

-3 -2 -1 1 2 3 -1 -0.5 0.5 1

-1
-0.5

-2
-1

-3

Figure 4.5 – Rose-curves of equation ρ(θ) = 1 + e · cos nθ.

sage: g1.show(aspect_ratio=1); g2.show(aspect_ratio=1)

Exercise 12. Draw the family of Pascal conchoids of polar equation ρ(θ) = a + cos θ
when the parameter a varies from 0 to 2 by steps of 0.1.

4.1.4 Curve Defined by an Implicit Equation


To draw a curve given by an implicit equation, you need to call the function
implicit_plot(f(x, y), (x, a, b), (y, c, d)); however, the complex_plot
command may also be used, which enables us to draw in colour the level set of
a two-variable
  function.
 Let us draw the curve given by the implicit equation
C = z ∈ C , cos(z 4 ) = 1 .
sage: z = var('z')
sage: g1 = complex_plot(abs(cos(z^4))-1,
....: (-3,3), (-3,3), plot_points=400)
sage: f = lambda x, y : (abs(cos((x + I * y) ** 4)) - 1)
sage: g2 = implicit_plot(f, (-3, 3), (-3, 3), plot_points=400)
sage: g1.show(aspect_ratio=1); g2.show(aspect_ratio=1)

4.1.5 Data Plot


To construct a bar graph, two distinct functions are available. On the one hand,
bar_chart takes as input an integer list and draws vertical bars whose height is
given by the list elements (in the given order). The width option enables us to
choose the bar width.
sage: bar_chart([randrange(15) for i in range(20)])
sage: bar_chart([x^2 for x in range(1,20)], width=0.2)
On the other hand, to draw the histogram of a random variable from a list of
floating-point numbers, we use the plot_histogram function. The list values are
first sorted and grouped into intervals (the number of intervals is given by the
80 CHAP. 4. GRAPHICS

-3 -2 -1 1 2 3

-1

-2

-3

 
Figure 4.6 – Curve g2 defined by the equation cos(z 4 ) = 1.

9 350
8
300
7
6 250

5 200
4 150
3
100
2
1 50

2 4 6 8 10 5 10 15

Figure 4.7 – Bar graphs.

option bins whose default value is 50), the height of each bar being proportional
to the number of corresponding values.
sage: liste = [10 + floor(10*sin(i)) for i in range(100)]
sage: bar_chart(liste)
sage: finance.TimeSeries(liste).plot_histogram(bins=20)

0.16

0.14
15
0.12

0.1

10
0.08

0.06

5 0.04

0.02

10 20 30 40 50 5 10 15

(a) Plot with bar_chart. (b) Plot with plot_histogram.

It often arises that the list of data values we want to study is stored in a
spreadsheet format. The Python csv package enables us to import such data
4.1. 2D GRAPHICS 81

stored in the csv format. For example, let us assume that we want to plot the
histogram of grades of a school class, which are in column 3 of the file exam01.csv.
To extract the grades from this column, we will use the following instructions (in
general, the first lines of such a file contain text, therefore we deal with potential
non-matching lines with the try keyword):
sage: import csv
sage: reader = csv.reader(open("exam01.csv"))
sage: grades = []; list = []
sage: for line in reader: 0.25

....: grades.append(line[2]) 0.2

....: for i in grades: 0.15

....: try: 0.1

....: f = float(i)
0.05

....: except ValueError:


0

....: pass 2 4 6 8 10 12 14

....: else:
....: list.append(f)
sage: finance.TimeSeries(list).plot_histogram(bins=20)
To draw a list of linked points (resp. non-linked), we use the line(p)
(resp. point(p) or points(p)) command, p being a list of 2-element lists (or
tuples) giving abscissa and ordinate of the points.
Example. (Random walk) Starting from the origin O, a particle moves a
distance ℓ every t seconds, in a random direction, independently of the preceding
moves. Let us draw an example of particle trajectory. The red line goes from the
initial to the final position.
sage: n, l, x, y = 10000, 1, 0, 0; p = [[0, 0]]
sage: for k in range(n):
....: theta = (2 * pi * random()).n(digits=5)
....: x, y = x + l * cos(theta), y + l * sin(theta)
....: p.append([x, y])
sage: g1 = line([p[n], [0, 0]], color='red', thickness=2)
sage: g1 += line(p, thickness=.4); g1.show(aspect_ratio=1)
Example. (Uniformly distributed sequences) Given a real sequence (un )n∈N∗ ,
we construct the polygonal line whose successive vertices are the points in the
complex plane X
zN = e2iπun .
n≤N

If the sequence is uniformly distributed modulo 1, the polygonal line should


behave like a random walk, and thus not go too far from the origin. Hence we
can conjecture the uniform distribution modulo 1 from the graphical aspect of
the polygonal line. Let us study the following cases:

• un = n 2 and N = 200,

• un = n ln(n) 2 and N = 10000,
82 CHAP. 4. GRAPHICS

50

40

30

20

10

-60 -40 -20 20 40

-10

-20

-30

Figure 4.8 – Random walk.


• un = ⌊n ln(n)⌋ 2 and N = 10000,

• un = pn 2 and N = 10000 (here pn is the n-th prime).

Figure 4.9 is obtained as follows (here for un = n 2):
sage: length = 200; n = var('n')
sage: u = lambda n: n * sqrt(2)
sage: z = lambda n: exp(2 * I * pi * u(n)).n()
sage: vertices = [CC(0, 0)]
sage: for n in range(1, length):
....: vertices.append(vertices[n - 1] + CC(z(n)))
sage: line(vertices).show(aspect_ratio=1)
We see that the curve√4.9a is amazingly regular, which suggests that the
uniform√ distribution of n 2 modulo 1 is deterministic. In the case of un =
n ln(n) 2, the values apparently seem random modulo 1. However, the associated
curve 4.9b is remarkably well structured. The curve 4.9c has the same kind
of structure as the second one. Finally,
√ the curve 4.9d shows the completely
different nature of primes modulo 1/ 2: the spirals have disappeared and the
aspect looks very similar to a random walk un (Figure 4.8). It thus looks as
though “prime numbers make use of all the randomness they are given...”
For a detailed interpretation of these curves, we refer the reader to the book
(in French) Les nombres premiers of Gérald Tenenbaum and Michel Mendès
France [TMF00].
Exercise 13 (Drawing terms of a recurrent sequence). We consider the sequence
(un )n∈N defined by: nu0 = a,
 
∀n ∈ N, un+1 = u2n − 14 .

 Represent graphically the behaviour of the  sequence by constructing a list of points


[u0 , 0], [u0 , u1 ], [u1 , u1 ], [u1 , u2 ], [u2 , u2 ], . . . , with a ∈ {−0.4, 1.1, 1.3}.

4.1.6 Displaying Solutions of Differential Equations


We can combine the above commands to represent solutions of differential equa-
tions or systems. To solve symbolically an ordinary differential equation, one calls
4.1. 2D GRAPHICS 83

60

0.6

40
0.4

0.2 20

-1 -0.8 -0.6 -0.4 -0.2


-50 -40 -30 -20 -10 10 20

-0.2
-20

-0.4

√ √
(a) Case un = n 2. (b) Case un = n ln(n) 2.

50

40

40

30 30

20

20
10

10
-80 -60 -40 -20 20

-10

-20 -30 -20 -10 10 20

-30
-10

-40

√ √
(c) Case un = ⌊n ln(n)⌋ 2. (d) Case un = pn 2.

Figure 4.9 – Uniformly distributed sequences.


84 CHAP. 4. GRAPHICS

the desolve function, which is studied in more detail in Chapter 10. To solve
a differential equation numerically, Sage provides several tools: desolve_rk4
(which uses the same syntax as desolve, and which is enough to solve differential
equations at undergraduate level), odeint (which calls the SciPy package), and
finally ode_solver (which calls the GSL library, and whose use is detailed in
Section 14.2). The functions desolve_rk4 and odeint return a list of points,
which is easy to draw using the line command; we will use them in this section
to draw numerical solutions.
Example. (First-order linear differential equation) Let us draw the integral
curves of the differential equation xy ′ − 2y = x3 .
sage: x = var('x'); y = function('y')
sage: DE = x*diff(y(x), x) == 2*y(x) + x^3
sage: desolve(DE, [y(x),x])
(_C + x)*x^2
sage: sol = []
sage: for i in srange(-2, 2, 0.2):
....: sol.append(desolve(DE, [y(x), x], ics=[1, i]))
....: sol.append(desolve(DE, [y(x), x], ics=[-1, i]))
sage: g = plot(sol, x, -2, 2)
sage: y = var('y')
sage: g += plot_vector_field((x, 2*y+x^3), (x,-2,2), (y,-1,1))
sage: g.show(ymin=-1, ymax=1)
To decrease the computation time, it would be better here to define “by hand”
the general solution of the equation, and to create a list of particular solutions (as
done in the solution of Exercise 14), instead of solving the differential equation
several times with different initial conditions. We could also compute a numerical
solution of this equation (with the desolve_rk4 function) to draw its integral
curves:
sage: x = var('x'); y = function('y')
sage: DE = x*diff(y(x), x) == 2*y(x) + x^3
sage: g = Graphics() # creates an empty graph
sage: for i in srange(-2, 2, 0.2):
....: g += line(desolve_rk4(DE, y(x), ics=[1, i],\

1 1

0.5 0.5

-2 -1.5 -1 -0.5 0.5 1 1.5 2 -2 -1.5 -1 -0.5 0.5 1 1.5 2

-0.5 -0.5

-1 -1

(a) Symbolic solution. (b) Numerical solution.

Figure 4.10 – Integral curves of xy ′ − 2y = x3 .


4.1. 2D GRAPHICS 85

10

-4 -2 2 4

-2

Figure 4.11 – Integral curves of y ′ (t) + cos(y(t) · t) = 0.

....: step=0.05, end_points=[0,2]))


....: g += line(desolve_rk4(DE, y(x), ics=[-1, i],\
....: step=0.05, end_points=[-2,0]))
sage: y = var('y')
sage: g += plot_vector_field((x, 2*y+x^3), (x,-2,2), (y,-1,1))
sage: g.show(ymin=-1, ymax=1)
As seen in the above example, the desolve_rk4 function takes as input a
differential equation (or the right-hand side f of the equation in explicit form
y ′ = f (y, x)), the name of the unknown function, the initial conditions, the step
and interval where a solution is sought. The optional argument output enables
us to specify the type of output: the default value ’list’ returns a list (which is
useful if we want to combine graphics as in our example), ’plot’ outputs the
graph of the solution, and ’slope_field’ adds the graphs of slopes of integral
curves.
Exercise 14. Draw the integral curves of the equation x2 y ′ − y = 0, for −3 ≤ x ≤ 3
and −5 ≤ y ≤ 5.
Let us now give an example of the odeint function from the SciPy package.
Example. (First-order non-linear differential equation) Let us draw the
integral curves of the equation y ′ (t) + cos(y(t) · t) = 0.
sage: import scipy; from scipy import integrate
sage: f = lambda y, t: - cos(y * t)
sage: t = srange(0, 5, 0.1); p = Graphics()
sage: for k in srange(0, 10, 0.15):
....: y = integrate.odeint(f, k, t)
....: p += line(zip(t, flatten(y)))
86 CHAP. 4. GRAPHICS

sage: t = srange(0, -5, -0.1); q = Graphics()


sage: for k in srange(0, 10, 0.15):
....: y = integrate.odeint(f, k, t)
....: q += line(zip(t, flatten(y)))
sage: y = var('y')
sage: v = plot_vector_field((1, -cos(x*y)), (x,-5,5), (y,-2,11))
sage: g = p + q + v; g.show()
The odeint function takes as argument the right-hand side f of the differential
equation y ′ = f (assumed to be in explicit form), one or more initial condi-
tions, and the interval where a solution is sought; it returns an array of type
numpy.ndarray that one converts using the flatten command1 already seen
in §3.3.2. The obtained list is then combined with the array t using the zip
command, and the approximate solution is displayed. To add the vector fields
tangent to the integral curves, we have used the plot_vector_field command.
Example. (Lotka-Volterra predator-prey model) We wish to represent graph-
ically the variation of a set of prey and predators evolving according to a system
of Lotka-Volterra equations:

 du
 = au − buv,
dt
 dv = −cv + dbuv,

dt
where u is the number of preys (for example rabbits), v is the number of predators
(for example foxes). In addition, the parameters a, b, c, d describe the evolution
of the populations: a is the natural growth of rabbits without foxes to eat them,
b is the decrease of rabbits when foxes kill them, c is the decrease of foxes without
any rabbit to eat, and finally d indicates how many rabbits are needed for a new
fox to appear.
sage: import scipy; from scipy import integrate
sage: a, b, c, d = 1., 0.1, 1.5, 0.75
sage: def dX_dt(X, t=0): # returns the population variation
....: return [a*X[0] - b*X[0]*X[1], -c*X[1] + d*b*X[0]*X[1]]
sage: t = srange(0, 15, .01) # time scale
sage: X0 = [10, 5] # initial conditions: 10 rabbits and 5 foxes
sage: X = integrate.odeint(dX_dt, X0, t) # numerical solution
sage: rabbits, foxes = X.T # shortcut for X.transpose()
sage: p = line(zip(t, rabbits), color='red') # number of rabbits graph
sage: p += text("Rabbits",(12,37), fontsize=10, color='red')
sage: p += line(zip(t, foxes), color='blue') # idem for foxes
sage: p += text("Foxes",(12,7), fontsize=10, color='blue')
sage: p.axes_labels(["time", "population"]); p.show(gridlines=True)
The instructions above show the evolution of the number of rabbits and foxes with
time (Figure 4.12, left), and those below the vector field (Figure 4.12, right):
1 We could also use the NumPy ravel function, which avoids creating a new object, and thus

optimises the memory usage.


4.1. 2D GRAPHICS 87

sage: n = 11; L = srange(6, 18, 12 / n); R = srange(3, 9, 6 / n)


sage: CI = zip(L, R) # list of initial conditions
sage: def g(x,y):
....: v = vector(dX_dt([x, y])) # for a nicer graph, we
....: return v/v.norm() # normalise the vector field
sage: x, y = var('x, y')
sage: q = plot_vector_field(g(x, y), (x, 0, 60), (y, 0, 36))
sage: for j in range(n):
....: X = integrate.odeint(dX_dt, CI[j], t) # resolution
....: q += line(X, color=hue(.8-float(j)/(1.8*n))) # graph plot
sage: q.axes_labels(["rabbits","foxes"]); q.show()

population 35
40
Rabbits 30
35

25
30

20
25
foxes

20 15

15 10

10 5
Foxes
5 0

0 2 4 6 8 10 12 14
time 0 10 20 30
rabbits
40 50 60

Figure 4.12 – Study of a predator-prey system.

Exercise 15 (Predator-prey model). Recreate the left-hand graph of Figure 4.12


using desolve_system_rk4 instead of odeint.
Exercise 16 (An autonomous differential system). Draw the integral curves of the
following differential system: n
ẋ = y,
ẏ = 0.5y − x − y 3 .
Exercise 17 (Flow around a cylinder with Magnus effect). We combine a simple flow
around a cylinder of radius a to a vortex of parameter α, which modifies the orthoradial
velocity component. We work in a coordinate system centered on the cylinder, with
cylindrical coordinates in the plane z = 0, i.e., in polar coordinates. The velocity
components are then:
   
a2 a2 αav0
vr = v0 cos(θ) 1 − and vθ = −v0 sin(θ) 1 + +2 .
r2 r2 r

The flow lines (which are identical to trajectories, since the flow is stationary) are parallel
to the velocity. We search a parametric expression of the flow lines; we have thus to
solve the differential system:
dr dθ vθ
= vr and = .
dt dt r
By using coordinates scaled by the radius a of the cylinder, we may assume a = 1. Draw
the flow lines for α ∈ {0.1, 0.5, 1, 1.25}.
88 CHAP. 4. GRAPHICS

The Magnus effect was proposed to build propulsion systems made from large vertical
rotating cylinders able to produce a longitudinal thrust when the wind is perpendicular
to the ship (this was the case of the Baden-Baden rotor ship built by Anton Flettner,
which crossed the Atlantic in 1926).

4.1.7 Evolute of a Curve


We now give an example of drawing the evolute of a parametric arc (let us recall
that the evolute is the envelope of the normals of a curve, or equivalently, the
locus of centres of curvature).
Example. (Evolute of the parabola) Let us find the equation of the evolute of
the parabola P of equation y = x2 /4, and show on the same graph the parabola P,
some normals to P and its evolute.
To determine a system of parametric equations (x(t), y(t)) of the evolute of a
family of lines ∆t defined by cartesian equations of the form α(t)X +β(t)Y = γ(t),
we express the fact that the line ∆t is tangent to the envelope at (x(t), y(t)):
α(t)x(t) + β(t)y(t) = γ(t), (4.1)
α(t)x′ (t) + β(t)y ′ (t) = 0. (4.2)

The derivative of Equation (4.1), combined with (4.2), yields the system:

α(t) x(t) + β(t) y(t) = γ(t), (4.1)


α′ (t)x(t) + β ′ (t)y(t) = γ ′ (t). (4.3)
In our case, the normal (Nt ) to the parabola P in M (t, t2 /4) has normal vector

→v = (1, t/2) (which is tangent to the parabola); it thus has for equation:
   
x−t 1 t t3
2 · = 0 ⇐⇒ x + y = t + ,
y − t /4 t/2 2 8
in other words, (α(t), β(t), γ(t)) = (1, t/2, t + t3 /8). We can then solve the
preceding system with the solve function:
sage: x, y, t = var('x, y, t')
sage: alpha(t) = 1; beta(t) = t / 2; gamma(t) = t + t^3 / 8
sage: env = solve([alpha(t) * x + beta(t) * y == gamma(t),\
....: diff(alpha(t), t) * x + diff(beta(t), t) * y == \
....: diff(gamma(t), t)], [x,y])
 
1 3
x = − t3 , y = t2 + 2
4 4
This gives a parametric representation of the normal envelope:

x(t) = − 41 t3 ,
y(t) = 2 + 43 t2 .
We can then answer the given question, by drawing some normals to the parabola
(more precisely, we draw line segment [M, M + 18−

n ] where M (u, u2 /4) is a point


on P and n = (−u/2, 1) a normal vector to P):
4.1. 2D GRAPHICS 89

sage: f(x) = x^2 / 4


sage: p = plot(f, -8, 8, rgbcolor=(0.2,0.2,0.4)) # the parabola
sage: for u in srange(0, 8, 0.1): # normals to the parabola
....: p += line([[u, f(u)], [-8*u, f(u) + 18]], thickness=.3)
....: p += line([[-u, f(u)], [8*u, f(u) + 18]], thickness=.3)
sage: p += parametric_plot((env[0][0].rhs(),env[0][1].rhs()),\
....: (t, -8, 8),color='red') # draws the evolute
sage: p.show(xmin=-8, xmax=8, ymin=-1, ymax=12, aspect_ratio=1)

12

10

-8 -6 -4 -2 2 4 6 8

Figure 4.13 – The parabola evolute.

As recalled above, the evolute of a curve is also the locus of its centres of curvature.
Using the circle function, let us draw some osculating circles of the parabola.
The centre of curvature Ω at a point Mt = (x(t), y(t)) of the curve has coordinates:

x′2 + y ′2 x′2 + y ′2
xΩ = x + −y ′ , and yΩ = y + x′ ,
x′ y ′′− x′′ y ′ x′ y ′′
− x′′ y ′

and the radius of curvature2 at Mt is:


3
(x′2 + y ′2 ) 2
R= .
x′ y ′′ − x′′ y ′

sage: t = var('t'); p = 2
sage: x(t) = t; y(t) = t^2 / (2 * p); f(t) = [x(t), y(t)]
sage: df(t) = [x(t).diff(t), y(t).diff(t)]
sage: d2f(t) = [x(t).diff(t, 2), y(t).diff(t, 2)]
2 We consider here the algebraic radius of curvature, which can be negative.
90 CHAP. 4. GRAPHICS

Type of drawing
Graph of a function plot
Parametric curve parametric_plot
Curve defined by a polar equation polar_plot
Curve defined by an implicit equation implicit_plot
Level set of a complex function complex_plot
Empty graphical object Graphics()
Integral curves of a differential equation odeint, desolve_rk4
Bar graph, bar chart bar_chart
Histogram of a statistical sequence plot_histogram
Polygonal chain line
Cloud of points points
Circle circle
Polygon polygon
Text text

Table 4.1 – Summary of 2D graphical functions.

sage: T(t) = [df(t)[0] / df(t).norm(), df[1](t) / df(t).norm()]


sage: N(t) = [-df(t)[1] / df(t).norm(), df[0](t) / df(t).norm()]
sage: R(t) = (df(t).norm())^3 / (df(t)[0]*d2f(t)[1]-df(t)[1]*d2f(t)[0])
sage: Omega(t) = [f(t)[0] + R(t)*N(t)[0], f(t)[1] + R(t)*N(t)[1]]
sage: g = parametric_plot(f(t), (t,-8,8), color='green',thickness=2)
sage: for u in srange(.4, 4, .2):
....: g += line([f(t=u), Omega(t=u)], color='red', alpha = .5)
....: g += circle(Omega(t=u), R(t=u), color='blue')
sage: g.show(aspect_ratio=1,xmin=-12,xmax=7,ymin=-3,ymax=12)

12

10

-10 -5 5

-2

Figure 4.14 – Osculating circles of the parabola.


4.2. 3D CURVES 91

Table 4.1 gives a summary of the functions detailed in this section. It also
contains the text command which enables us to add a character string in a graph,
and the polygon command to plot polygons.

4.2 3D Curves
Sage provides the plot3d(f(x,y),(x,a,b),(y,c,d)) command to display sur-
faces in 3-dimensions. The surface obtained may then be visualised via the Jmol
application; the Tachyon 3D Ray Tracer or three.js can be used alternatively
with the option viewer=’tachyon’ or viewer=’threejs’ of the show command.
Here is a first example of parametric surface (Figure 4.15):
sage: u, v = var('u, v')
sage: h = lambda u,v: u^2 + 2*v^2
sage: plot3d(h, (u,-1,1), (v,-1,1), aspect_ratio=[1,1,1])

Figure 4.15 – The parametric surface (u, v) 7→ u2 + 2v 2 .

Displaying the surface corresponding to a 2-variable function helps us to study


that function, as will be seen in the following example.
Example. (A discontinuous function whose directional derivatives exist ev-
erywhere!) Study the existence in (0, 0) of the directional derivatives and the
continuity of the function f from R2 to R defined by:
( 2
x y
4 2 if (x, y) 6= (0, 0),
f (x, y) = x +y
0 if (x, y) = (0, 0).

For H = cos θ
sin θ , the function ϕ(t) = f (tH) = f (t cos θ, t sin θ) is differentiable in
t = 0 for any value of θ; indeed,
sage: f(x, y) = x^2 * y / (x^4 + y^2)
sage: t, theta = var('t, theta')
sage: limit(f(t * cos(theta), t * sin(theta)) / t, t=0)
cos(theta)^2/sin(theta)
92 CHAP. 4. GRAPHICS

Hence f has well-defined directional derivatives in any direction at the point (0, 0).
To better visualise the surface corresponding to f , we can first look for some level
sets; for example the level set of value 12 :
sage: solve(f(x,y) == 1/2, y)
[y == x^2]
sage: a = var('a'); h = f(x, a*x^2).simplify_rational(); h
a/(a^2 + 1)
Along the parabola of equation y = ax2 , except at the origin, f has thus a
a a
constant value f (x, ax2 ) = 1+a2 . We then display the function h : a −
7 → 1+a2:

sage: plot(h, a, -4, 4)

0.4

0.2

-4 -3 -2 -1 1 2 3 4

-0.2

-0.4

Figure 4.16 – A vertical cut of the surface under study.

The function h has its maximum at a = 1 and its minimum at a = −1. The
restriction of f to the parabola of equation y = x2 corresponds to the level set
at “height” 12 ; conversely, the restriction to the parabola of equation y = −x2
corresponds to the bottom of the “thalweg” at height − 21 . In conclusion, arbitrarily
close to the point (0, 0), we can find points where f takes as value 12 , or respectively
− 12 . As a consequence, f is not continuous at the origin.
sage: p = plot3d(f(x,y),(x,-2,2),(y,-2,2),plot_points=[150,150])
We might also draw horizontal planes to display the level sets of this function
with:
sage: for i in range(1,4):
....: p += plot3d(-0.5 + i / 4, (x, -2, 2), (y, -2, 2),\
....: color=hue(i / 10), opacity=.1)
Among the other 3D graphical commands, implicit_plot3d allows us to
display surfaces defined by an implicit equation of the form f (x, y, z) = 0. Let
us display for example the Cassini surface (Figure 4.18a) defined by the implicit
2
equation: a2 + x2 + y 2 = 4 a2 x2 + z 4 .
sage: x, y, z = var('x, y, z'); a = 1
sage: h = lambda x, y, z:(a^2 + x^2 + y^2)^2 - 4*a^2*x^2-z^4
sage: implicit_plot3d(h, (x,-3,3), (y,-3,3), (z,-2,2),\
....: plot_points=100)
4.2. 3D CURVES 93

x2 y
Figure 4.17 – The surface corresponding to f : (x, y) 7→ x4 +y 2
.

Finally, let us give an example of 3-dimensional curve (Figure 4.18b) with the
line3d command:
sage: line3d([(-10*cos(t)-2*cos(5*t)+15*sin(2*t),\
....: -15*cos(2*t)+10*sin(t)-2*sin(5*t),\
....: 10*cos(3*t)) for t in srange(0,6.4,.1)],radius=.5)

(a) The Cassini surface. (b) A knot in space.

Figure 4.18 – Surface and curve in 3D.


94 CHAP. 4. GRAPHICS
Computational Domains
5
Writing mathematics on paper or on the blackboard requires a compromise
between ease of notations and rigour. The same holds for the day-to-day use of a
computer algebra system. Sage tries to give this choice to the user, by letting
her/him specify, more or less rigorously, the computational domains: what is the
nature of the considered objects, in which sets do they live, which operations can
be applied to them?

5.1 Sage is Object-Oriented


Python and Sage use heavily the object-oriented programming paradigm. Even
though this remains relatively transparent in common use, it is useful to know a
little about this paradigm, which is quite natural in a mathematical context.

5.1.1 Objects, Classes and Methods


The object-oriented programming paradigm consists in modelling each physical or
abstract entity one wishes to manipulate by a programming language construction
called an object. In most cases, as in Python, each object is an instance of a class.
For example, the rational number 12/35 is represented by an object which is an
instance of the Rational class:
sage: o = 12/35
sage: type(o)
<type 'sage.rings.rational.Rational'>
Note that this class is really associated to the object 12/35, and not to the variable
o in which it is stored:
96 CHAP. 5. COMPUTATIONAL DOMAINS

sage: type(12/35)
<type 'sage.rings.rational.Rational'>

Let us be more precise. An object is a part of the computer memory which


stores the required information to represent the corresponding entity. The class
in turn defines two things:
1. the data structure of an object, i.e., how the information is organised in
memory. For example, the Rational class specifies that a rational number
like 12/35 is represented by two integers: its numerator and its denominator;
2. its behaviour, in particular the available operations on this object: how to
obtain the numerator of a rational number, how to compute its absolute
value, how to multiply or add two rational numbers. Each of these opera-
tions is implemented by a method (here respectively numer, abs, __mul__,
__add__).
To factor an integer, we will thus call the factor method with the following
syntax:
sage: o = 720
sage: o.factor()
2^4 * 3^2 * 5
which we can read as follows: “take the value of o and apply to it the factor
method, without any other argument”. Under the hood, Python performs the
following computation:
sage: type(o).factor(o)
2^4 * 3^2 * 5
From left to right: “request from the class of o (type(o)) the factorisation method
(type(o).factor), and apply it to o”.
Please note that we can apply this method not only to a variable, but also
directly to a value:
sage: 720.factor()
2^4 * 3^2 * 5
and thus we can chain the operations, from left to right. Here, we first take the
numerator of a rational number, then we factor this numerator:
sage: o = 720 / 133
sage: o.numerator().factor()
2^4 * 3^2 * 5
To make the user’s life easier, Sage also provides a function factor, so that
factor(o) is a shortcut for o.factor(). It is the case for several common
functions, and it is possible to add our own shortcuts, as illustrated in the
following exercise.
Exercise 18. Build a shortcut ndigits so that ndigits(o) calls the ndigits
method of the object o.
5.1. SAGE IS OBJECT-ORIENTED 97

5.1.2 Objects and Polymorphism


Almost all Sage operations are polymorphic, i.e., they apply to several kinds of
objects. For example, whatever the nature of the object o that we want to “factor”,
we will use the same notation o.factor() (or its shortcut factor(o)). The
computations to be performed however differ to factor an integer or a polynomial!
They also differ if the polynomial has rational coefficients, or coefficients in a
finite field. The object class determines the version of the factor code that will
be called.
Similarly, like the usual mathematical notation, the product of two objects a
and b can always be denoted a*b, even if the algorithm used differs in each case1 .
Here is a product of two integers:
sage: 3 * 7
21
a product of two rational numbers, obtained by multiplying the numerators and
denominators, then reducing the fraction:
sage: (2/3) * (6/5)
4/5
a product of two complex numbers, using the relation i2 = −1:
sage: (1 + I) * (1 - I)
2
some commutative products of two formal expressions:
sage: (x + 2) * (x + 1)
(x + 2)*(x + 1)
sage: (x + 1) * (x + 2)
(x + 2)*(x + 1)
Apart from the notation simplicity, this form of polymorphism enables us to
write generic programs which apply to any object having the involved operations
(here multiplication):
sage: def fourth_power(a):
....: a = a * a
....: a = a * a
....: return a

sage: fourth_power(2)
16
sage: fourth_power(3/2)
81/16
1 For a binary operation like the product, the selection of the appropriate method is slightly

more complex than what was described above. Indeed, we might deal with mixed operations
like the sum 2 + 3/4 of an integer and of a rational number. In this case, 2 will be converted in
the rational 2/1, and the addition of two rationals will be called. The rules that describe which
operand must be converted, and how it should be converted, are part of the coercion model.
98 CHAP. 5. COMPUTATIONAL DOMAINS

sage: fourth_power(I)
1
sage: fourth_power(x+1)
(x + 1)^4
sage: M = matrix([[0,-1],[1,0]]); M
[ 0 -1]
[ 1 0]
sage: fourth_power(M)
[1 0]
[0 1]

5.1.3 Introspection
Python objects, and therefore Sage objects, have some introspection features. This
means that, during execution, we can “ask” an object for its class, its methods,
etc., and manipulate the obtained informations using the usual constructions
of the programming language. For instance, the class of an object o is itself a
Python object, and we can obtain it using type(o):
sage: t = type(5/1); t
<type 'sage.rings.rational.Rational'>
sage: t == type(5)
False

We see here that the expression 5/1 constructs the rational number 5, which
differs — as Python object — from the integer 5!
The introspection tools also give access to the factorisation on-line help from
an object of integer type:
sage: o = 720
sage: o.factor?
Docstring:
Return the prime factorization of this integer as a formal
Factorization object.
...
and even to the source code of that function:
sage: o.factor??
...
def factor(self, algorithm='pari', proof=None, ...)
...
if algorithm == 'pari':
...
elif algorithm in ['kash', 'magma']:
...
Avoiding some technical details, we see here that Sage delegates the integer
factorisation to other tools (PARI/GP, Kash, or Magma).
5.2. ELEMENTS, PARENTS, CATEGORIES 99

In the same vein, we can use automatic completion to interactively “ask” an


object o which operations can be applied to it:
sage: o.n<tab>
o.n o.nbits o.ndigits
o.next_prime o.next_prime_power o.next_probable_prime
o.nth_root o.numerator o.numerical_approx
Once again, it is a form of introspection.

5.2 Elements, Parents, Categories


5.2.1 Elements and Parents
In the preceding section, we have seen the concept of class of an object. In
practice, it is enough to know that this notion exists; we rarely have to explicitly
look for the type of an object. However, Sage introduces another concept closer
to mathematics: the parent of an object, that we will detail now.
Assume for example that we want to know if an element a is invertible. The
answer does not only depend on the element itself, but also on the mathematical
set A it belongs to (and its potential inverse). For example, the number 5 is not
invertible in the set Z of integers, since its inverse 1/5 is not an integer:
sage: a = 5; a
5
sage: a.is_unit()
False
However, it is invertible in the set of rational numbers:
sage: a = 5/1; a
5
sage: a.is_unit()
True
Sage gives two different answers to that question since, as seen in the above
section, the objects 5 and 5/1 have different classes.
In some object-oriented computer algebra systems, like MuPAD or Axiom,
the mathematical set X to which x belongs (here Z or Q) is simply the class of
x. Sage follows the approach of the Magma system, and defines the set X by
another object attached to x, called its parent:
sage: parent(5)
Integer Ring
sage: parent(5/1)
Rational Field
We can obtain these two sets with the following shortcuts:
sage: ZZ
Integer Ring
100 CHAP. 5. COMPUTATIONAL DOMAINS

sage: QQ
Rational Field
and use them to easily convert an element from one set to the other, when it
makes sense:
sage: QQ(5).parent()
Rational Field
sage: ZZ(5/1).parent()
Integer Ring
sage: ZZ(1/5)
Traceback (most recent call last):
...
TypeError: no conversion of this rational to integer
More generally, the P(x) syntax — where P is a parent — tries to convert
the object x into an element of P. We show four different instances of 1: as
integer 1 ∈ Z, as rational number 1 ∈ Q, as real floating-point 1.0 ∈ R or complex
floating-point 1.0 + 0.0i ∈ C:
sage: ZZ(1), QQ(1), RR(1), CC(1)
(1, 1, 1.00000000000000, 1.00000000000000)
Exercise 19. Find two Sage objects having the same type and different parents.
Then find two Sage objects having the same parent and different types.

5.2.2 Constructions
The parents being themselves first-class objects, we can apply operations to them.
For example, one can construct the cartesian product Q2 :
sage: cartesian_product([QQ, QQ])
The Cartesian product of (Rational Field, Rational Field)
find Q as the fraction field of Z:
sage: ZZ.fraction_field()
Rational Field
construct the ring of polynomials in x with coefficients in Z:
sage: ZZ['x']
Univariate Polynomial Ring in x over Integer Ring
Using an incremental approach, we can construct complex algebraic structures
like the 3 × 3 matrix space with polynomial coefficients on a finite field:
sage: Z5 = GF(5); Z5
Finite Field of size 5
sage: P = Z5['x']; P
Univariate Polynomial Ring in x over Finite Field of size 5
sage: M = MatrixSpace(P, 3, 3); M
5.3. DOMAINS WITH A NORMAL FORM 101

Full MatrixSpace of 3 by 3 dense matrices over


Univariate Polynomial Ring in x over Finite Field of size 5
and draw a random element from this domain:
sage: M.random_element()
[2*x^2 + 3*x + 4 4*x^2 + 2*x + 2 4*x^2 + 2*x]
[ 3*x 2*x^2 + x + 3 3*x^2 + 4*x]
[ 4*x^2 + 3 3*x^2 + 2*x + 4 2*x + 4]

5.2.3 Further Reading: Categories


In general, a parent does not itself have a parent, but a category that indicates
its properties:
sage: QQ.category()
Join of Category of number fields and Category of quotient fields and
Category of metric spaces
Sage knows that Q is a field:
sage: QQ in Fields()
True
and thus, for instance, an additive and commutative group (see Figure 5.1):
sage: QQ in CommutativeAdditiveGroups()
True
Since Q is a field, Q[x] is a Euclidean ring:
sage: QQ['x'] in EuclideanDomains()
True
All these properties are used to provide rigorous and efficient computations
on elements of these sets.

5.3 Domains with a Normal Form


Let us now browse some of the parents we will encounter in Sage.
We have seen in §2.1 how important normal forms2 can be in computer
algebra, since they allow to determine if two objects are mathematically equal in
comparing their normal form representations. Each of the fundamental parents
presented in this section corresponds to a domain with normal form, i.e., a set
of mathematical objects having a normal form. This allows Sage to represent
without any ambiguity the elements of each of these parents3 .
2 In this book we use both canonical form and normal form to mean that two objects are

mathematically identical if their canonical (or normal) forms are equal. Sometimes normal form
is meant as a weaker notion, where only zero is assumed to have a unique representation.
3 Most of the other parents available in Sage correspond to domains with a normal form, but

not all of them. It also happens that, for efficiency reasons, Sage represents elements in normal
form only when explicitly requested.
102 CHAP. 5. COMPUTATIONAL DOMAINS

fields

euclidean domains

principal ideal domains division rings

unique factorization domains

gcd domains

integral domains

commutative rings domains

rings

rngs semirings

commutative additive groups monoids

semigroups commutative additive monoids

commutative additive semigroups

magmas additive magmas

sets

sets with partial maps

objects

Figure 5.1 – A short part of the category graph in Sage.


5.3. DOMAINS WITH A NORMAL FORM 103

Some basic Python types


Python integers int
Python floating-point numbers float
Booleans (true, false) bool
Character strings str

Basic numerical domains


Integers Z ZZ or IntegerRing()
Rational numbers Q QQ or RationalField()
Floating-point numbers with p bits Reals(p) or RealField(p)
Complex floating-point numbers with p bits Complexes(p) or ComplexField(p)

Rings and finite fields


Integers modulo n, Z/nZ Integers(n) or IntegerModRing(n)
Finite field Fq GF(q) or FiniteField(q)

Algebraic numbers
Algebraic numbers Q̄ QQbar or AlgebraicField()
Real algebraic numbers AA or AlgebraicRealField()
Number fields Q[x]/hpi NumberField(p)

Symbolic computation
Matrices m × n with coefficients in A MatrixSpace(A, m, n)
Polynomials A[x, y] A['x,y'] or PolynomialRing(A, 'x,y')
Series A[[x]] A[['x']] or PowerSeriesRing(A, 'x')
Symbolic expressions SR

Table 5.1 – Main domains and parents.

5.3.1 Elementary Domains


We call elementary computational domains (or simply elementary domains) the
classical sets of constants, with no variable: integers, rational numbers, floating-
point numbers, booleans, integers modulo n...

Integers. The integers are represented in radix two internally, and printed by
default in radix ten. As seen above, the Sage integers are objects of the class
Integer. Their parent is the ring Z:
sage: 5.parent()
Integer Ring
The integers are always in normal form; their equality is thus easy to check. As
a consequence, to be able to represent integers in factorised form, the factor
command needs a specific class:
sage: type(factor(4))
<class 'sage.structure.factorization_integer.IntegerFactorization'>
104 CHAP. 5. COMPUTATIONAL DOMAINS

The Integer class is specific to Sage: by default, Python uses integers of


type int. In general, the conversion from Integer to int — or vice versa — is
automatic, but it might be necessary to convert explicitly by
sage: int(5)
5
sage: type(int(5))
<type 'int'>

or conversely
sage: Integer(5)
5
sage: type(Integer(5))
<type 'sage.rings.integer.Integer'>

Rational Numbers. The normal form property extends to rational numbers,


elements of QQ, which are always represented in reduced form. Therefore, in the
command
sage: factorial(99) / factorial(100) - 1 / 50
-1/100
the factorials are first evaluated, then the obtained fraction 1/100 is put into
reduced form. Sage then constructs the rational number 1/50, performs the
subtraction, then reduces again the result (there is nothing to do here).

Floating-Point Numbers. Real numbers cannot all be exactly represented


in a finite format. Their numerical values are approximated by floating-point
numbers, which will be discussed in more detail in Chapter 11.
Within Sage, floating-point numbers are encoded in binary radix. As a
consequence, the floating-point number corresponding to the input 0.1 slightly
differs from 1/10, since 1/10 is not exactly representable in binary! Each floating-
point number has its own precision. The parent of floating-point numbers with p-
bit significand is denoted Reals(p), which for the default precision (p = 53) is
also denoted RR. As for integers, Sage floating-point numbers differ from their
Python analogue.
When they appear in a sum, product or quotient containing also integers
or rational numbers, floating-point numbers are “contagious”; the complete
expression is then evaluated as a floating-point number:
sage: 72/53 - 5/3 * 2.7
-3.14150943396227
Likewise, when the argument of some usual function is a floating-point number,
the result is again a floating-point number:
sage: cos(1), cos(1.)
(cos(1), 0.540302305868140)
5.3. DOMAINS WITH A NORMAL FORM 105

The numerical_approx method (or its alias n) evaluates numerically the


remaining expressions. An optional argument allows us to set the number of
significant digits used for this evaluation. Here is for example π with 50 significant
digits:
sage: pi.n(digits=50) # variant: n(pi,digits=50)
3.1415926535897932384626433832795028841971693993751

Complex Floating-Point Numbers. Similarly, the floating-point approxi-


mations of complex numbers with precision p are elements of Complexes(p) —
or its alias ComplexField(p) —, or CC with the default precision of 53 bits. For
example, we can construct a complex floating-point number and compute its
argument by
sage: z = CC(1,2); z.arg()
1.10714871779409

Complex symbolic expressions

The imaginary unit i (denoted I or i), already encountered in the preced-


ing chapters, is not an element of CC, but a symbolic expression (see §5.4.1):
sage: I.parent()
Symbolic Ring
We can use it to define a complex floating-point number with an explicit
conversion:
sage: (1.+2.*I).parent()
Symbolic Ring
sage: CC(1.+2.*I).parent()
Complex Field with 53 bits of precision
In the world of symbolic expressions, the methods real, imag and abs
give respectively the real part, the imaginary part and the modulus of a
complex number:
sage: z = 3 * exp(I*pi/4)
sage: z.real(), z.imag(), z.abs().canonicalize_radical()
(3/2*sqrt(2), 3/2*sqrt(2), 3)

Booleans. Logic expressions also form a computational domain with normal


form, but the class of boolean values is a basic type without specific parent in
Sage. The two normal forms are True and False (or true and false):
sage: a, b, c = 0, 2, 3
sage: a == 1 or (b == 2 and c == 3)
True
106 CHAP. 5. COMPUTATIONAL DOMAINS

In tests and loops, the conditions built from the operators or and and are evaluated
lazily from left to right. This means that the evaluation of a condition or ends
as soon as the first True value is encountered, without evaluating the rightmost
terms; similarly with and and False. Hence the following divisibility test of b by
a does not produce any error even if a = 0:
sage: a = 0; b = 12; (a == 0 and b == 0) or (a != 0 and b % a == 0)

The operator not takes precedence over and, which in turn takes precedence
over or, the equality and comparison tests having precedence over all boolean
operators. The two following tests are thus equivalent to the above one:
sage: ((a == 0) and (b == 0)) or ((a != 0) and (b % a == 0))
sage: a == 0 and b == 0 or not a == 0 and b % a == 0

In addition, Sage allows multiple equality or inequality tests, exactly like in


mathematics:

x≤y<z≤t encoded by x <= y < z <= t


x = y = z 6= t x == y == z != t

In the simple cases, these tests are automatically performed; otherwise we call
the bool command to force the evaluation:
sage: x, y = var('x, y')
sage: bool( (x-y)*(x+y) == x^2-y^2 )
True

Integers Modulo n. To define an integer modulo n, we first build its parent,


the ring Z/nZ:
sage: Z4 = IntegerModRing(4); Z4
Ring of integers modulo 4
sage: m = Z4(7); m
3

As in the case of floating-point numbers, the computations involving m are done


modulo 4 via automatic conversions. In the following example, 3 and 1 are
automatically converted in elements of Z/4Z:
sage: 3 * m + 1
2

When p is prime, we can also choose to build Z/pZ as a field:


sage: Z3 = GF(3); Z3
Finite Field of size 3

Both IntegerModRing(n) and GF(p) are domains with a normal form: the
reduction modulo n or p are done automatically. The computations in rings and
finite fields are detailed in Chapter 6.
5.3. DOMAINS WITH A NORMAL FORM 107

5.3.2 Compound Domains


From well-defined constants, some classes of symbolic objects with variables and
having a normal form can be constructed. The most important such classes are
matrices, polynomials, rational functions and truncated power series.
The corresponding parents are parameterised by their coefficient domain. For
example, matrices with integer coefficients differ from matrices with coefficients
in Z/nZ, and the corresponding computation rules are automatically applied,
without requiring an explicit call to a function reducing integers modulo n.
Part II of this book is mainly dedicated to these objects.

Matrices. The normal form4 of a matrix is obtained when all its coefficients
are themselves in normal form. As a consequence, a matrix defined over a field or
ring with normal form is automatically in normal form:
sage: a = matrix(QQ, [[1,2,3],[2,4,8],[3,9,27]])
sage: (a^2 + 1) * a^(-1)
[ -5 13/2 7/3]
[ 7 1 25/3]
[ 2 19/2 27]
The matrix function call is a shortcut. Internally, Sage builds the corresponding
parent, here the space of 3 × 3 matrices with coefficients in Q (which has normal
form), then uses it to construct the matrix:
sage: M = MatrixSpace(QQ,3,3); M
Full MatrixSpace of 3 by 3 dense matrices over Rational Field
sage: a = M([[1,2,3],[2,4,8],[3,9,27]])
sage: (a^2 + 1) * a^(-1)
[ -5 13/2 7/3]
[ 7 1 25/3]
[ 2 19/2 27]
The operations on symbolic matrices are described in Chapter 8, and on numerical
matrices in Chapter 13.

Polynomials and Fractions. Like matrices, polynomials in Sage “know” the


type of their coefficients. Their parents are polynomial rings like Z[x] or C[x, y, z],
presented in detail in Chapters 7 and 9, and which can be built as follows:
sage: P = ZZ['x']; P
Univariate Polynomial Ring in x over Integer Ring
sage: F = P.fraction_field(); F
Fraction Field of Univariate Polynomial Ring in x over Integer Ring
sage: p = P(x+1) * P(x); p
x^2 + x
sage: p + 1/p
4 Do not confuse this concept of normal form with the normal forms of a matrix viewed as a

linear transformation, which will be discussed in Chapter 8.


108 CHAP. 5. COMPUTATIONAL DOMAINS

(x^4 + 2*x^3 + x^2 + 1)/(x^2 + x)


sage: parent(p + 1/p)
Fraction Field of Univariate Polynomial Ring in x over Integer Ring

As we will see in §5.4.2, there is no optimal representation for polynomials and


fractions. The elements of polynomial rings are represented in expanded form.
These rings do therefore have a normal form as soon as the coefficients themselves
belong to a domain with normal form.
These polynomials differ from the polynomial expressions (Symbolic Ring)
we have seen in Chapter 2, which do not have a well-defined coefficient type,
neither a parent reflecting such a type. The latter give an alternative to “true”
polynomials, which can be useful, for example, to mix polynomials and other
mathematical expressions. However, contrary to polynomial rings, when we work
with such expressions, we have to explicitly call a reduction command like expand
to put them in normal form (if such a form exists).

Power Series. Truncated power series are objects of the form

a0 + a1 x + a2 x2 + · · · + an xn + O(xn+1 )

used for example to represent Taylor expansions, and whose usage in Sage is
described in §7.5. The parent of series in x, truncated at order n, and with
coefficients in A, is the ring A[[x]], build with PowerSeriesRing(A, ’x’, n).
Like polynomials, truncated power series have an analogue in the world SR of
symbolic expressions. The corresponding command to reduce to normal form is
series.
sage: f = cos(x).series(x == 0, 6); 1 / f
1
1+(− 12 )x2 + 24
1 4
x +O(x6 )

sage: (1 / f).series(x == 0, 6)

1 + 21 x2 + 5 4
24 x + O x6

Algebraic Numbers. An algebraic number is defined as root of a polynomial.


When the polynomial degree is 5 or more, in general it is
√ not possible to explicitly
write its roots in terms of the operations +, −, ×, /, ·. However, many com-
putations involving the roots can be performed successfully without any other
information than the polynomial itself.
sage: k.<a> = NumberField(x^3 + x + 1); a^3; a^4+3*a
-a - 1
-a^2 + 2*a

This book does not describe in detail how to play with algebraic numbers in Sage,
however several examples can be found in Chapters 7 and 9.
5.4. EXPRESSIONS VS COMPUTATIONAL DOMAINS 109

5.4 Expressions vs Computational Domains


Several approaches are thus possible for manipulating objects like polynomials
within Sage. We can consider them as particular symbolic expressions, as in the
first chapters, or introduce a given ring of polynomials and compute with its
elements. To conclude this chapter, we briefly describe the parent of symbolic
expressions, the SR domain, then we demonstrate through several examples how
important it is to control the domain of computations, and the differences between
both approaches.

5.4.1 Symbolic Expressions as a Computational Domain


Symbolic expressions themselves form a computational domain. In Sage, their
parent is the symbolic ring:
sage: parent(sin(x))
Symbolic Ring
that can also be obtained with:
sage: SR
Symbolic Ring
The properties of this ring are rather fuzzy; it is commutative:
sage: SR.category()
Category of commutative rings
and the computation rules assume roughly speaking that all symbolic variables
are in C.
The form of expressions in SR (polynomials, fractions, trigonometric expres-
sions) being not apparent in their class or parent, the result of a computation
often requires some manual transformations to obtain the desired form (see §2.1),
by using for example expand, combine, collect and simplify. To use these
functions well we have to know which kind of transformation they perform, to
which sub-classes5 of symbolic expressions these transformations apply, and which
of these sub-classes have a normal form. In particular, the blind use of the
simplify command can yield wrong results. Some variants of simplify allow
then to precisely describe the transformation to apply.

5.4.2 Examples: Polynomials and Normal Forms


Let us build the ring Q[x1 , x2 , x3 , x4 ] of polynomials in 4 variables:
sage: R = QQ['x1,x2,x3,x4']; R
Multivariate Polynomial Ring in x1, x2, x3, x4 over Rational Field
sage: x1, x2, x3, x4 = R.gens()
The elements of R are automatically put in expanded form:

5 In the sense of subset, and not of Python class.


110 CHAP. 5. COMPUTATIONAL DOMAINS

sage: x1 * (x2 - x3)


x1*x2 - x1*x3
which, as we have seen, is a normal form. In particular, the test to zero in R is
trivial:
sage: (x1+x2)*(x1-x2) - (x1^2 - x2^2)
0
An expanded form is not
Q always optimal. For example, if we build the
Vandermonde determinant 1≤i<j≤n (xi − xj ):

sage: prod( (a-b) for (a,b) in Subsets([x1,x2,x3,x4],2) )


x1^3*x2^2*x3 - x1^2*x2^3*x3 - x1^3*x2*x3^2 + x1*x2^3*x3^2
+ x1^2*x2*x3^3 - x1*x2^2*x3^3 - x1^3*x2^2*x4 + x1^2*x2^3*x4
+ x1^3*x3^2*x4 - x2^3*x3^2*x4 - x1^2*x3^3*x4 + x2^2*x3^3*x4
+ x1^3*x2*x4^2 - x1*x2^3*x4^2 - x1^3*x3*x4^2 + x2^3*x3*x4^2
+ x1*x3^3*x4^2 - x2*x3^3*x4^2 - x1^2*x2*x4^3 + x1*x2^2*x4^3
+ x1^2*x3*x4^3 - x2^2*x3*x4^3 - x1*x3^2*x4^3 + x2*x3^2*x4^3
we obtain 4! = 24 terms. The same construct with an expression from SR remains
under factored form, and is much more compact and readable:
sage: x1, x2, x3, x4 = SR.var('x1, x2, x3, x4')
sage: prod( (a-b) for (a,b) in Subsets([x1,x2,x3,x4],2) )
-(x1 - x2)*(x1 - x3)*(x1 - x4)*(x2 - x3)*(x2 - x4)*(x3 - x4)

In addition, a factored representation allows faster gcd computations. However, it


would be unwise to put automatically every polynomial into factored form, even
if this is also a normal form, since the factorisation is computationally expensive,
and makes additions costly.
In general, depending on the kind of computation, the optimal representation
of an element is not always its normal form (if it exists). This leads computer
algebra systems to a compromise with expressions. Some basic simplifications, like
the reduction of fractions or the multiplication by zero, are done automatically; the
other transformations are left to the user with the provided specialised commands.

5.4.3 Example: Polynomial Factorisation


Let us consider the factorisation of the following polynomial expression:
sage: x = var('x')
sage: p = 54*x^4+36*x^3-102*x^2-72*x-12
sage: factor(p)
6*(x^2 - 2)*(3*x + 1)^2
Is this answer satisfying? It is indeed a factorisation of p, however its completeness
heavily depends on the context! For now, Sage considers p as a symbolic expression,
which happens to be polynomial. Sage cannot know if we wish to factor p as a
5.4. EXPRESSIONS VS COMPUTATIONAL DOMAINS 111

product of polynomials with integer coefficients, or with rational coefficients (for


example).
To take full control, we will make it clear in which mathematical set (i.e.,
computational domain) p lives. To start, let us consider p as a polynomial with
integer coefficients. We thus define the ring R = Z[x] of these polynomials:
sage: R = ZZ['x']; R
Univariate Polynomial Ring in x over Integer Ring
Then we convert p in this ring:
sage: q = R(p); q
54*x^4 + 36*x^3 - 102*x^2 - 72*x - 12
The output seems identical, however q knows it is an element of R:
sage: parent(q)
Univariate Polynomial Ring in x over Integer Ring
As a consequence, its factorisation is uniquely defined:
sage: factor(q)
2 * 3 * (3*x + 1)^2 * (x^2 - 2)
Let us proceed similarly in the rational field:
sage: R = QQ['x']; R
Univariate Polynomial Ring in x over Rational Field
sage: q = R(p); q
54*x^4 + 36*x^3 - 102*x^2 - 72*x - 12
sage: factor(q)
(54) * (x + 1/3)^2 * (x^2 - 2)
In this new context, the factorisation is again well-defined, but different from the
previous one.
Let us now compute a complete factorisation over the complex numbers. A
first solution is to allow a numerical approximation of complex numbers with 16
bits of precision:
sage: R = ComplexField(16)['x']; R
Univariate Polynomial Ring in x over Complex Field
with 16 bits of precision
sage: q = R(p); q
54.00*x^4 + 36.00*x^3 - 102.0*x^2 - 72.00*x - 12.00
sage: factor(q)
(54.00) * (x - 1.414) * (x + 0.3333)^2 * (x + 1.414)

Another solution is to extend the field of rational numbers, e.g., adding 2.
sage: R = QQ[sqrt(2)]['x']; R
Univariate Polynomial Ring in x over Number Field in sqrt2
with defining polynomial x^2 - 2
sage: q = R(p); q
112 CHAP. 5. COMPUTATIONAL DOMAINS

54*x^4 + 36*x^3 - 102*x^2 - 72*x - 12


sage: factor(q)
(54) * (x - sqrt2) * (x + sqrt2) * (x + 1/3)^2
Finally, maybe we want that coefficients be considered modulo 5?
sage: R = GF(5)['x']; R
Univariate Polynomial Ring in x over Finite Field of size 5
sage: q = R(p); q
4*x^4 + x^3 + 3*x^2 + 3*x + 3
sage: factor(q)
(4) * (x + 2)^2 * (x^2 + 3)

5.4.4 Synthesis
In the preceding examples, we have shown how the user might control the level
of rigour in her/his computations.
On the one hand, she/he can use symbolic expressions. These expressions
live in the ring SR. They offer several methods (presented in Chapter 2) which
apply well to some sub-classes of expressions, like polynomial expressions. When
we recognise to which classes a given expression belongs to, this helps to know
which functions could be applied. The simplification of expressions is a particular
problem where this recognition is crucial. The main classes of expression are
defined to take into account this simplification issue, and we will prefer this
approach in the rest of this book.
On the other hand, the user can construct a parent which will explicitly define
the computational domain. It is especially interesting when this parent has a
normal form: i.e., when two objects are mathematically equal if and only if they
have the same representation.
As a summary, the main advantage of symbolic expressions (SR) is their ease
of use: no explicit declaration of the computational domain, easy addition of new
variables or functions, easy change of the computational domain (for example
when one takes the sine of a polynomial expression), use of all possible calculus
tools (integration, etc.). The advantages of explicitly defining the computational
domain are in the first place pedagogical, more rigorous computations6 , the
automatic normal form transformation (which can also be a drawback!), and
the easy access to advanced constructions that would be difficult with symbolic
expressions (computations in a finite field or an algebraic extension of Q, in a
non-commutative ring, etc.).

6 Sage is not a certified computer algebra system: a bug is thus always possible; however,

there will be no use of implicit assumption.


Part II

Algebra and Symbolic


Computation
God made the integers, all else is the work of man.
Leopold Kronecker (1823 - 1891)

Finite Fields and Elementary Number


6
Theory

This chapter describes the use of Sage for elementary number theory, for working
with objects related to finite fields (§6.1), for primality testing (§6.2) and integer
factorisation (§6.3); we will also discuss some applications (§6.4).

6.1 Finite Fields and Rings


Finite rings and fields are basic objects, both in number theory and throughout
computer algebra. Indeed, many algorithms in computer algebra involve compu-
tations over finite fields, where one can exploit the information obtained using
techniques such as Hensel lifting, or reconstruction using the Chinese Remainder
Theorem. As an example, we can mention the Cantor-Zassenhaus algorithm
for factoring univariate polynomials with integer coefficients, which begins by
factoring the polynomial over a finite field.

6.1.1 The Ring of Integers Modulo n


In Sage, the ring Z/nZ of integers modulo n is defined using the constructor
IntegerModRing (or, more simply, Integers). All objects constructed using this
constructor and those derived from them are systematically reduced modulo n,
and so have a canonical (or normal) form: that is to say, two variables representing
the same value modulo n also have the same internal representation. In certain
very special situations, it may be more efficient to delay these reductions modulo
n; for example, if one multiplies matrices with such coefficients, one would then
rather work with integers, and carry out the reductions modulo n “by hand”
116 CHAP. 6. FINITE FIELDS AND NUMBER THEORY

using a % n. Note that the modulus n does not appear explicitly in the displayed
value:
sage: a = IntegerModRing(15)(3); b = IntegerModRing(17)(3); a, b
(3, 3)
sage: a == b
False
One consequence of this is that when one uses “cut-and-paste” to copy integers
modulo n, one loses information about n. Given a variable whose value is
an integer modulo n, one can recover information about n using the methods
base_ring or parent, and the value of n using the method characteristic:
sage: R = a.parent(); R
Ring of integers modulo 15
sage: R.characteristic()
15
The basic operations (addition, subtraction and multiplication) are overloaded for
integers modulo n, and call the appropriate functions; also, integers are converted
automatically when one of the operands is an integer modulo n:
sage: a + a, a - 17, a * a + 1, a^3
(6, 1, 10, 12)
For inversion, 1/a mod n, or division, b/a mod n, Sage carries out the operation
if possible; otherwise, i.e., when a and n have a nontrivial common factor, a
ZeroDivisionError is raised:
sage: 1/(a+1)
4
sage: 1/a
Traceback (most recent call last):
...
ZeroDivisionError: Inverse does not exist.
To obtain the value of a as an integer from its residue a mod n, one can use
the method lift or even ZZ:
sage: z = a.lift(); y = ZZ(a); y, type(y), y == z
(3, <type 'sage.rings.integer.Integer'>, True)

The additive order of a modulo n is the smallest integer k > 0 such that
ka = 0 mod n. It is equal to k = n/g, where g = gcd(a, n), and is given by the
method additive_order (we will see later that one can also use Mod or mod to
define integers modulo n):
sage: [Mod(x,15).additive_order() for x in range(0,15)]
[1, 15, 15, 5, 15, 3, 5, 15, 15, 5, 3, 15, 5, 15, 15]
The multiplicative order of a modulo n, for a coprime1 to n, is the smallest
integer k > 0 such that ak = 1 mod n. (If a had a common divisor p with n, then
1 “coprime” and “relatively prime” are synonymous.
6.1. FINITE FIELDS AND RINGS 117

ak mod n would be a multiple of p for all k.) If this multiplicative order equals
ϕ(n), which is the order of the multiplicative group modulo n, one says that a
is a generator of this group. Thus for n = 15, there is no generator, since the
maximal order is 4 < 8 = ϕ(15):
sage: [[x, Mod(x,15).multiplicative_order()]
....: for x in range(1,15) if gcd(x,15) == 1]
[[1, 1], [2, 4], [4, 2], [7, 4], [8, 4], [11, 2], [13, 4], [14, 2]]
Here is an example with n = p prime, where 3 is a generator:
sage: p = 10^20 + 39; mod(2,p).multiplicative_order()
50000000000000000019
sage: mod(3,p).multiplicative_order()
100000000000000000038
An important operation on Z/nZ is modular exponentiation, which means
to calculate ae mod n. The RSA crypto-system relies on this operation. To
calculate ae mod n, the most efficient algorithms require of the order of log e
multiplications or squarings modulo n. It is crucial to reduce all calculations
modulo n systematically, and not compute ae first as an integer, as the following
example shows:
sage: n = 3^100000; a = n-1; e = 100
sage: %timeit (a^e) % n
5 loops, best of 3: 387 ms per loop
sage: %timeit power_mod(a,e,n)
125 loops, best of 3: 3.46 ms per loop

6.1.2 Finite Fields


Finite fields2 are defined using the constructor FiniteField, or more simply
GF. As well as constructing prime fields GF(p) with p prime, one can construct
non-prime finite fields GF(q) with q = pk , where p is prime and k > 1 an integer.
As with rings, objects created in such a field have a canonical representation,
and reduction is carried out at each arithmetic operation. Finite fields have the
same properties as rings (§6.1.1), with in addition the possibility of inverting each
non-zero element:
sage: R = GF(17); [1/R(x) for x in range(1,17)]
[1, 9, 6, 13, 7, 3, 5, 15, 2, 12, 14, 10, 4, 11, 8, 16]
A non-prime finite field Fpk with p prime and k > 1 is isomorphic to the
quotient ring of polynomials in Fp [x] modulo a monic irreducible polynomial f
of degree k. In this case, Sage will provide a name for the generator of the field,
that is, the variable x, or the user can provide a name:

2 The finite field with q elements is either denoted F , or GF(q) (where “GF” stands for
q
“Galois Field”). Here we will use the notation Fq for the mathematical object, and the notation
GF(q) in Sage code.
118 CHAP. 6. FINITE FIELDS AND NUMBER THEORY

sage: R = GF(9,name='x'); R
Finite Field in x of size 3^2
Here, Sage has automatically chosen the polynomial f :
sage: R.polynomial()
x^2 + 2*x + 2
Field elements are thus represented by polynomials in the generator x, ak−1 xk−1 +
· · · + a1 x + a0 , with coefficients ai which are elements of Fp :
sage: Set([r for r in R])
{0, 1, 2, x, x + 1, x + 2, 2*x, 2*x + 1, 2*x + 2}
One can also make Sage use a specific irreducible polynomial f :
sage: Q.<x> = PolynomialRing(GF(3))
sage: R2 = GF(9, name='x', modulus=x^2+1); R2
Finite Field in x of size 3^2
Be careful: even though the two fields R and R2 created above are both
isomorphic to F9 , Sage provides no isomorphism between them automatically:
sage: p = R(x+1); R2(p)
Traceback (most recent call last):
...
TypeError: unable to coerce from a finite field other than the prime
subfield

6.1.3 Rational Reconstruction


The problem of rational reconstruction is a useful application of modular methods.
Given a residue a modulo m, it involves finding a “small” rational number x/y
such that x/y ≡ a mod m. If one knows that such a small rational number
exists, instead of computing x/y directly as a rational number, one may instead
compute x/y modulo m, which gives the residue a, and then one recovers x/y via
rational reconstruction. This second approach is often more efficient, since one has
replaced computations with rationals, possibly involving costly gcd calculations,
by modular calculations.
Lemma. Let a, m ∈ N, with 0 < a < m. There exists at most one p pair of
coprime integers x, y ∈ Z such that x/y ≡ a mod m with 0 < |x|, y ≤ m/2.
Such a pair x, y does not always exist: for example, take a = 2 and m =
5. The rational reconstruction algorithm is based on the extended Euclidean
algorithm. The extended gcd of m and a computes a sequence of integers
ai = αi m + βi a, where the ai are decreasing, and the coefficients αp i , βi increase in
absolute value. It therefore suffices to stop as soon as |ai |, |βi | ≤ m/2, and the
solution is then x/y = ai /βi . This algorithm is implemented in the Sage function
rational_reconstruction, which returns x/y when a solution exists, raising an
error if not:
sage: rational_reconstruction(411,1000)
6.1. FINITE FIELDS AND RINGS 119

-13/17
sage: rational_reconstruction(409,1000)
Traceback (most recent call last):
...
ArithmeticError: rational reconstruction of 409 (mod 1000) does not
exist

To illustrate the use of rational reconstruction, consider the computation of


the Harmonic numbers Hn = 1 + 1/2 + · · · + 1/n. A naive calculation using
rational numbers would be as follows:
sage: def harmonic(n):
....: return add([1/x for x in range(1,n+1)])

Now we know that Hn can be written in the form pn /qn with integers pn , qn ,
where qn = lcm(1, 2, . . . , n). We also know that Hn ≤ log n + 1, which allows us
to bound pn . This leads to the following function, which finds Hn using modular
arithmetic and rational reconstruction:
sage: def harmonic_mod(n,m):
....: return add([1/x % m for x in range(1,n+1)])
sage: def harmonic2(n):
....: q = lcm(range(1,n+1))
....: pmax = RR(q*(log(n)+1))
....: m = ZZ(2*pmax^2)
....: m = ceil(m/q)*q + 1
....: a = harmonic_mod(n,m)
....: return rational_reconstruction(a,m)

In this example, the function harmonic2 is no more efficient than the original
function harmonic, but it illustrates the method. It is not always necessary to
know a rigorous bound for x and y, as a rough estimate “by eye” will suffice,
provided that one is able to verify easily that x/y is the correct solution.
One can generalise the method of rational reconstruction to handle numerators
x and denominators y of different sizes; see for example Section 5.10 of the book
[vzGG03].

6.1.4 The Chinese Remainder Theorem


Another useful application of modular arithmetic involves the use of the Chinese
Remainder Theorem, or CRT, commonly called “Chinese remaindering”. Given
two coprime moduli m and n, and two residue classes a mod m and b mod n,
we seek an integer x such that x ≡ a mod m and x ≡ b mod n. The Chinese
Remainder Theorem enables us to recover x uniquely modulo the product mn. To
see how this works, one deduces from x ≡ a mod m that x has the form x = a+λm
with λ ∈ Z. Substituting into x ≡ b mod n, one obtains λ ≡ λ0 mod n, where
λ0 = (b − a)/m mod n. Hence x = x0 + µnm, where x0 = a + λ0 m, and µ is an
arbitrary integer.
120 CHAP. 6. FINITE FIELDS AND NUMBER THEORY

Here we have presented the simplest variant of the Chinese Remainder Theorem.
One can also consider the case of several moduli m1 , m2 , . . . , mk . The Sage
command for finding x0 , given a, b, m, n, is crt(a,b,m,n):
sage: a = 2; b = 3; m = 5; n = 7; lambda0 = (b-a)/m % n; a + lambda0 * m
17
sage: crt(2,3,5,7)
17

Let us return to the computation of Hn . We first compute Hn mod mi for


i = 1, 2, . . . , k, and then obtain Hn mod m1 · · · mk by Chinese remaindering,
finally recovering the value of Hn by rational reconstruction:
sage: def harmonic3(n):
....: q = lcm(range(1,n+1))
....: pmax = RR(q*(log(n)+1))
....: B = ZZ(2*pmax^2)
....: a = 0; m = 1; p = 2^63
....: while m < B:
....: p = next_prime(p)
....: b = harmonic_mod(n,p)
....: a = crt(a,b,m,p)
....: m = m*p
....: return rational_reconstruction(a,m)
sage: harmonic(100) == harmonic3(100)
True

The Sage function crt may also be used when the moduli m and n are not
coprime. If g = gcd(m, n), then a solution exists if and only if a ≡ b mod g:
sage: crt(15,1,30,4)
45
sage: crt(15,2,30,4)
Traceback (most recent call last):
...
ValueError: No solution to crt problem since gcd(30,4) does not divide
15-2

A more complicated application of the Chinese Remainder Theorem is given in


Exercise 23.

6.2 Primality
Testing whether an integer is prime is a fundamental operation for a symbolic
computer software package. Even if the user is not aware of it, such tests are
carried out thousands of times per second by the software. For example, to factor
a polynomial in Z[x], one starts by factoring it in Fp [x] for some prime number p,
and one must therefore find a suitable prime.
6.2. PRIMALITY 121

Useful commands
Ring of integers modulo n IntegerModRing(n)
Finite field with q elements GF(q)
Pseudo-primality test is_pseudoprime(n)
Primality test is_prime(n)

Table 6.1 – Review.

There are two main classes of primality test. The most efficient are pseudo-
primality tests, and are in general based on forms of Fermat’s Little Theorem,
which says that if p is prime, then every integer a with 0 < a < p is an element
of the multiplicative group (Z/pZ)∗ , and hence ap−1 ≡ 1 mod p. One uses
small values of a (2, 3, . . .) to speed up the computation of ap−1 mod p. If
ap−1 6≡ 1 mod p, then p is certainly not prime. If ap−1 ≡ 1 mod p, one cannot
conclude either that p is or is not prime; we say that p is a (Fermat) pseudo-prime
to base a. The intuition is that an integer p which is a pseudo-prime to many
bases has a greater chance of being prime (but see below). Pseudo-primality
tests share the property that when they return the verdict False, the number
is certainly composite, whereas when they return True, no definite conclusion is
possible.
The second class consists of true primality tests. These tests always return a
correct answer, but can be less efficient than pseudo-primality tests, especially
for numbers that are pseudo-primes to many bases, and in particular for actual
primes. Many software packages only provide pseudo-primality tests, despite the
name of the corresponding function (isprime, for example) sometimes leading
the user to believe that a true primality test is provided. Sage provides two
different functions: is_pseudoprime for pseudo-primality, and is_prime for true
primality:
sage: p = previous_prime(2^400)
sage: %timeit is_pseudoprime(p)
625 loops, best of 3: 1.07 ms per loop
sage: %timeit is_prime(p)
5 loops, best of 3: 485 ms per loop
We see in this example that the primality test is more costly; when possible,
therefore, one prefers to use is_pseudoprime.
Some primality testing algorithms provide a certificate, which allows an
independent subsequent verification of the result, often more efficiently than the
test itself. Sage does not provide such a certificate in the current release, but one
can construct one using Pocklington’s Theorem:

Theorem. Let n > 1 be an odd integer such that n − 1 = F R, with F ≥ n.
n−1
If for each prime factor p of F , there exists a such that a ≡ 1 mod n and
a(n−1)/p − 1 is coprime to n, then n is prime.
Consider for example n = 231 − 1. The factorisation of n − 1 is 2 · 32 · 7 · 11 · 31 ·
151 · 331. One can take F = 151 · 331, and a = 3 satisfies the condition for both
122 CHAP. 6. FINITE FIELDS AND NUMBER THEORY

factors p = 151 and p = 331. Hence it suffices to prove the primality of 151 and
331 in order to deduce that n is prime. This test uses modular exponentiation in
an important way.

Carmichael numbers

Carmichael numbers are composite integers n that are pseudo-primes to


all bases coprime to n. Fermat’s Little Theorem is insufficient to distinguish
these from primes, however many bases are tested. The smallest Carmichael
number is 561 = 3 · 11 · 17. A Carmichael number must have at least three
prime factors: for suppose that n = pq is a Carmichael number, with p, q
primes and p < q; by definition of Carmichael numbers, if a is a primitive
root modulo q then an−1 ≡ 1 modulo n implies that the same congruence
also holds modulo q, and hence that n − 1 is a multiple of q − 1. Then n must
be of the form q + λq(q − 1), since it is a multiple of q and n − 1 is a multiple
of q − 1; now n = pq implies p = λ(q − 1) + 1, which contradicts p < q. If
n = pqr, then n is a Carmichael number if an−1 ≡ 1 mod p, and similarly
modulo q and r, since then the Chinese Remainder Theorem implies that
an−1 ≡ 1 mod n. So a sufficient condition is that n − 1 is divisible by each
of p − 1, q − 1 and r − 1:
sage: [560 % (x-1) for x in [3,11,17]]
[0, 0, 0]

Exercise 20. Write a Sage function to count the Carmichael numbers n = pqr ≤ N ,
with p, q, r distinct odd primes. How many do you find for N = 104 , 105 , 106 , 107 ?
(Richard Pinch has counted 20138200 Carmichael numbers less than 1021 .)
Finally, in order to repeat an operation on all prime numbers in an interval, it
is better to employ the construction prime_range, which constructs a table of
primes using a sieve, than to simply use a loop with next_probable_prime or
next_prime:
sage: def count_primes1(n):
....: return add([1 for p in range(n+1) if is_prime(p)])
sage: %timeit count_primes1(10^5)
5 loops, best of 3: 674 ms per loop
The function is faster if one uses is_pseudoprime instead of is_prime:
sage: def count_primes2(n):
....: return add([1 for p in range(n+1) if is_pseudoprime(p)])
sage: %timeit count_primes2(10^5)
5 loops, best of 3: 256 ms per loop
In this example, it is worth using a loop rather than constructing a list of 105
elements, and again is_pseudoprime is faster than is_prime:
sage: def count_primes3(n):
....: s = 0; p = 2
6.3. FACTORISATION AND DISCRETE LOGARITHMS 123

....: while p <= n: s += 1; p = next_prime(p)


....: return s
sage: %timeit count_primes3(10^5)
5 loops, best of 3: 49.2 ms per loop
sage: def count_primes4(n):
....: s = 0; p = 2
....: while p <= n: s += 1; p = next_probable_prime(p)
....: return s
sage: %timeit count_primes4(10^5)
5 loops, best of 3: 48.6 ms per loop

Using the iterator prime_range is faster still:


sage: def count_primes5(n):
....: s = 0
....: for p in prime_range(n): s += 1
....: return s
sage: %timeit count_primes5(10^5)
125 loops, best of 3: 2.67 ms per loop

6.3 Factorisation and Discrete Logarithms


One says that an integer a is a square, or a quadratic residue, modulo n if
there exists x such that a ≡ x2 mod n. If not, one says that a is a quadratic
non-residue3 modulo n. When n = p is prime, there is a test to decide efficiently
whether a is a quadratic residue, using the computation of the Jacobi symbol
of a and p, denoted (a|p), which takes the values {−1, 0, 1}, where (a|p) = 0
when a is a multiple of p, and (a|p) = 1 (respectively, (a|p) = −1) when a is
(respectively, is not) a square modulo p. The complexity of computing the Jacobi
symbol (a|n) is essentially the same as that of computing the gcd of a and n,
namely O(M (ℓ) log ℓ) where ℓ is the size of n, and M (ℓ) is the cost of multiplying
two integers of size ℓ. However, implementations of Jacobi symbols — as of gcds
— do not all have this complexity (here, a.jacobi(n) computes (a|n)):
sage: p = (2^42737+1)//3; a = 3^42737
sage: %timeit a.gcd(p)
125 loops, best of 3: 4.3 ms per loop
sage: %timeit a.jacobi(p)
25 loops, best of 3: 26.1 ms per loop

When n is composite, finding solutions to x2 ≡ a mod n is as hard as factorising


n. Moreover, the Jacobi symbol, which is relatively simple to compute, only gives
partial information: if (a|n) = −1 then there is no solution, since the existence
of a solution implies (a|p) = 1 for all prime factors p of n, hence (a|n) = 1; but
(a|n) = +1 does not imply that a is a square modulo n when n is composite.
3 This terminology is traditional, though “non-quadratic residue” would be more logical.
124 CHAP. 6. FINITE FIELDS AND NUMBER THEORY

Let n be a positive integer, let g be a generator of the multiplicative group


modulo n (we assume here that n is such that this group is cyclic), and let a be
coprime to n. By definition of the fact that g is a generator, there is an integer x
such that g x = a mod n. The discrete logarithm problem consists of finding such
an integer x. The log method gives a solution to this problem:
sage: p = 10^10+19; a = mod(17,p); a.log(2)
6954104378
sage: mod(2,p)^6954104378
17
The best known algorithms for computing discrete logarithms have the same order
of complexity, as a function of the size of n, as those for factoring n. However,
the current implementation of discrete logarithms in Sage is not very efficient:
sage: p = 10^37+43; a = mod(17,p)
sage: time r = a.log(2)
CPU times: user 1min 32s, sys: 64 ms, total: 1min 32s
Wall time: 1min 34s

Aliquot sequences

The aliquot sequence associated to a positive integer n is the recurrent


sequence (sk ) defined by: s0 = n and sk+1 = σ(sk ) − sk , where σ(sk ) is the
sum of the positive divisors of n, i.e., sk+1 is the sum of the proper divisors
of sk , excluding sk itself. The iteration stops when sk = 1, so sk−1 is prime,
or when the sequence (sk ) enters a cycle. For example, starting from n = 30
one obtains:

30, 42, 54, 66, 78, 90, 144, 259, 45, 33, 15, 9, 4, 3, 1.

When the cycle has length one, we say that the starting integer is perfect, for
example 6 = 1 + 2 + 3 and 28 = 1 + 2 + 4 + 7 + 14 are perfect. When the
cycle has length two, the two integers in the cycle are called amicable and
form an amicable pair, for example 220 and 284. When the cycle has length
three or more, the integers in the cycle are called sociable.
Exercise 21. Calculate the aliquot sequence starting with 840, take the 5 first and
5 last terms, and draw the graph of log10 sk as a function of k (you can use the function
sigma).

6.4 Applications
6.4.1 The Constant δ
The constant δ is a two-dimensional generalisation of Euler’s constant γ. It is
defined as follows: !
X n
1
δ = lim − log n , (6.1)
n→∞ πrk2
k=2
6.4. APPLICATIONS 125

√ plane R containing
2
where rk is the radius of the smallest closed disc in the affine √ at
least k points of Z . For √
2
example, r2 = 1/2, r3 = r4 = 2/2, r5 = 1, r6 = 5/2,
r7 = 5/4, and r8 = r9 = 2:

Exercise 22 (Masser-Gramain constant). 1. Write a function which takes as input


a positive integer k, and returns the radius rk and the centre (xk , yk ) of a minimal
p disc,
of radius rk , containing at least k points of Z2 . You may assume that rk < k/π.
2. Write a function which draws the circle with centre (xk , yk ) and radius rk , together
with m ≥ k points of Z2 , as above.
3. Using the bounding inequalities
p √ r
π(k − 6) + 2 − 2 k−1
< rk < , (6.2)
π π

calculate an approximation of δ with an error at most 0.3.

6.4.2 Computation of a Multiple Integral


This application was inspired by the article [Bea09]. Let k and n1 , n2 , . . . , nk be
non-negative integers. We wish to compute the integral
Z
I= xn1 1 xn2 2 · · · xnk k dx1 dx2 . . . dxk ,
V

where the domain of integration is defined by V = {x1 ≥ x2 ≥ · · · ≥ xk ≥


0, x1 + · · · + xk ≤ 1}. For example, for k = 2, n1 = 3, n2 = 5, one finds the
value
Z 1/2 Z 1−x2
13
I= x31 x52 dx1 dx2 = .
x2 =0 x1 =x2 258048
126 CHAP. 6. FINITE FIELDS AND NUMBER THEORY

Exercise 23. Given that I is a rational number, develop an algorithm using rational
reconstruction and/or the Chinese Remainder Theorem to calculate I. Implement the
algorithm in Sage, and apply it to the case [n1 , . . . , n31 ] =

[9, 7, 8, 11, 6, 3, 7, 6, 6, 4, 3, 4, 1, 2, 2, 1, 1, 1, 2, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0].


7
Polynomials

This chapter will discuss univariate polynomials and related objects, mainly
rational functions and formal power series. We will first see how to perform with
Sage some transformations like the Euclidean division of polynomials, factorisation
into irreducible polynomials, root isolation, or partial fraction decomposition. All
these transformations will take into account the ring or field where the polynomial
coefficients live: Sage enables us to compute in polynomial rings A[x], in their
quotient A[x]/hP (x)i, in fraction fields K(x) or in formal power series rings A[[x]]
for a whole set of base rings.
Operations on polynomials also have some unexpected applications. How
would you automatically guess the next term of the sequence

1, 1, 2, 3, 8, 11, 39...?

For example, you could use the Padé approximation of rational functions, presented
in Section 7.4.3! How could you get a series expansion of the solutions of the
equation exf (x) = f (x)? An answer can be found in Section 7.5.3.
We assume in general that the reader is used to playing with polynomials and
rational functions at the first year university level. However, we will discuss more
advanced subjects. How to prove that the solutions of the equation x5 − x − 1
cannot be expressed by radicals? It suffices to compute its Galois group, as
explained in Section 7.3.4. The corresponding parts are not used elsewhere in this
book, and the reader may skip them. Finally, this chapter gives a few examples
with algebraic and p-adic numbers.
Here we will focus on polynomials with one variable, called univariate polyno-
mials. Multivariate polynomials are discussed in Chapter 9.
128 CHAP. 7. POLYNOMIALS

Playing with polynomial rings, R = A[x]


construction (dense repr.) R.<x> = A[] or R.<x> = PolynomialRing(A)
e.g. Z[x], Q[x], R[x], Z/nZ[x] ZZ['x'], QQ['x'], RR['x'], Integers(n)['x']
construction (sparse repr.) R.<x> = PolynomialRing(A, sparse=True)
accessing the base ring A R.base_ring()
accessing the variable x R.gen() or R.0
tests (integral, noetherian...) R.is_integral_domain(), R.is_noetherian(), ...

Table 7.1 – Polynomial rings.

7.1 Polynomial Rings


7.1.1 Introduction
We have seen in Chapter 2 how to perform computations on symbolic expressions,
elements of the “symbolic ring” SR. Some methods available for these expressions,
for example degree, are suited for polynomials:
sage: x = var('x'); p = (2*x+1)*(x+2)*(x^4-1)
sage: print("{} is of degree {}".format(p, p.degree(x)))
(x^4 - 1)*(2*x + 1)*(x + 2) is of degree 6
In some computer algebra systems, like Maple or Maxima, representing polyno-
mials as particular symbolic expressions is the usual way to play with them. Like
Axiom, Magma or MuPAD, Sage also lets you manipulate polynomials in a more
algebraic way, and “knows” how to compute in rings like Q[x] or Z/4Z [x, y, z].
Hence, to reproduce the above example in a well-defined polynomial ring,
we assign to the Python variable x the unknown of the polynomial ring in x
with rational coefficients, given by polygen(QQ, ’x’), instead of the symbolic
variable x returned1 by var(’x’):
sage: x = polygen(QQ, 'x'); p = (2*x+1)*(x+2)*(x^4-1)
sage: print("{} is of degree {}".format(p, p.degree()))
2*x^6 + 5*x^5 + 2*x^4 - 2*x^2 - 5*x - 2 is of degree 6
We notice that the polynomial is automatically expanded. The “algebraic” poly-
nomials are always represented in normal form. This is a crucial difference with
respect to the polynomials in SR. In particular, when two algebraic polynomi-
als are mathematically equal, their computer representation is the same, and a
comparison coefficient by coefficient is enough to check their equality.
The available functions on algebraic polynomials are much wider and more
efficient that those on (polynomial) symbolic expressions.

7.1.2 Building Polynomial Rings


Polynomials in Sage, like many other algebraic objects, generally have coefficients
in a commutative ring. This is the point of view of this book; however, most of
1 A little difference here: while var(’x’) is equivalent to x = var(’x’) in interactive use,

polygen(QQ, ’x’) alone does not change the value of the Python variable x.
7.1. POLYNOMIAL RINGS 129

the examples will have coefficients in a field. In the whole chapter, the letters A
and K respectively correspond to a commutative ring and to a field.
The first step to perform a computation in an algebraic structure R is often
to build R itself. We build Q[x] with

sage: R = PolynomialRing(QQ, 'x')


sage: x = R.gen()

The ’x’ on the first line is a character string, which is the name of the indeter-
minate, or generator of the ring. The x on the second line is a Python variable
in which one stores the generator; using the same name makes the code easier
to read. The object stored in the variable x represents the polynomial x ∈ Q[x].
Its parent (the parent of a Sage object is the algebraic structure “from which it
comes”, see §5.1) is the ring QQ[’x’]:

sage: x.parent()
Univariate Polynomial Ring in x over Rational Field

The polynomial x ∈ Q[x] is considered different from x ∈ A[x] for a base ring
A 6= Q, and also different from those, like t ∈ Q[t], whose indeterminate has a
different name.
The expression PolynomialRing(QQ, ’t’) might also be written QQ[’t’].
We often combine this abbreviation with the construction S.<g> = ..., which
simultaneously assigns a structure to the variable S and its generator to the
variable g. The construction of the ring Q[x] and of its indeterminate then reduces
to R.<x> = QQ[]. The form x = polygen(QQ, ’x’) seen above is equivalent to

sage: x = PolynomialRing(QQ, 'x').gen()

Let us mention that we can choose between several memory representations


when we construct a polynomial ring. The differences between representations
are discussed in §7.6.

Exercise 24 (Variables and indeterminates).

1. How would you define x and y to obtain the following results?

sage: x^2 + 1
y^2 + 1
sage: (y^2 + 1).parent()
Univariate Polynomial Ring in x over Rational Field

2. After the instructions

sage: Q.<x> = QQ[]; p = x + 1; x = 2; p = p + x

what is the value of p?


130 CHAP. 7. POLYNOMIALS

Polynomials with polynomial coefficients

In Sage, we can define polynomial rings with coefficients in any com-


mutative ring, including another polynomial ring. But beware that rings
A[x][y] constructed this way differ from the true polynomial rings with several
variables like A[x, y]. The latter, presented in Chapter 9, are better suited for
usual computations. Indeed, working in A[x][y][. . . ] introduces an asymmetry
between the variables.
However, in some cases we precisely want to have one main variable, and
the other variables as parameters. The polynomial method of multivariate
polynomials allows us to isolate one variable, more or less like the collect
method of symbolic expressions. For example, to compute the reciprocal of
a given polynomial with respect to one of its variables:
sage: R.<x,y,z,t> = QQ[]; p = (x+y+z*t)^2
sage: p.polynomial(t).reverse()
(x^2 + 2*x*y + y^2)*t^2 + (2*x*z + 2*y*z)*t + z^2
Here, p.polynomial(t) creates a univariate polynomial in the variable t and
with coefficients in QQ[x,y,z], to which we then apply the reverse method.
The other conversions between A[x, y, . . . ] and A[x][y][. . . ] work as ex-
pected:
sage: x = polygen(QQ); y = polygen(QQ[x], 'y')
sage: p = x^3 + x*y + y + y^2; p
y^2 + (x + 1)*y + x^3
sage: q = QQ['x,y'](p); q
x^3 + x*y + y^2 + y
sage: QQ['x']['y'](q)
y^2 + (x + 1)*y + x^3

7.1.3 Polynomials
Creation and Basic Arithmetic. After the instruction R.<x> = QQ[], the
expressions constructed from x and rational constants with operations + and *
are elements of Q[x]. For example, in p = x + 2, Sage automatically determines
that the values of the variable x and the integer 2 can both be seen as elements
of Q[x]. The addition routine of polynomials in Q[x] is thus called; it builds and
returns the polynomial x + 2 ∈ Q[x].
Another way to build a polynomial is to enumerate its coefficients:
sage: def rook_polynomial(n, var='x'):
....: return ZZ[var]([binomial(n, k)^2 * factorial(k)
....: for k in (0..n) ])
The above function constructs polynomials whose coefficient of xk is the number
of ways to put k rooks on an n × n chessboard, so that two rooks cannot
capture each other; this explains the name of the function. The parentheses after
ZZ[var] force the conversion of a given object into an element of this ring. The
7.1. POLYNOMIAL RINGS 131

Accessing data, syntactic operations


indeterminate x p.variables(), p.variable_name()
coefficient of xk p[k]
leading coefficient p.leading_coefficient()
degree p.degree()
list of coefficients p.list() or p.coefficients(sparse=False)
list of non-zero coefficients p.coefficients()
dictionary degree 7→ coefficient p.dict()
tests (monic, constant...) p.is_monic(), p.is_constant(), ...

Basic arithmetic
operations p + q, p − q, p × q, pk p + q, p - q, p * q, p^k
substitution x := a p(a) or p.subs(a)
derivative p.derivative() or p.diff()

Transformations
transformation of coefficients p.map_coefficients(f)
change of base ring A[x] → B[x] p.change_ring(B) or B['x'](p)
reciprocal polynomial p.reverse()

Table 7.2 – Basic operations on polynomials p, q ∈ A[x].

conversion of a list [a0 , a1 , . . . ] into an element of ZZ[’x’] yields the polynomial


a0 + a1 x + · · · ∈ Z[x].

Global View on Polynomial Operations. The elements of a polynomial ring


are represented by Python objects from the class Polynomial, or from derived
classes. The main operations2 available for these objects are summarised in
Tables 7.2 to 7.5. For example, we query the degree of a polynomial with the
degree method. Similarly, p.subs(a) or simply p(a) yields the value of p at the
point a, but also computes the composition p ◦ a when a itself is a polynomial,
and more generally evaluates a polynomial of A[x] at an element of an A-algebra:

sage: p = R.random_element(degree=4) # a random polynomial


sage: p
-4*x^4 - 52*x^3 - 1/6*x^2 - 4/23*x + 1
sage: p.subs(x^2)
-4*x^8 - 52*x^6 - 1/6*x^4 - 4/23*x^2 + 1
sage: p.subs(matrix([[1,2],[3,4]]))
[-375407/138 -273931/69]
[ -273931/46 -598600/69]
We will come back to the content of the last two tables in Sections 7.2.1 and 7.3.
2 There are many other operations. Those tables omit functions which are too advanced,

some specialised variants of methods we mention, and numerous methods common to all ring
elements, and even to all Sage objects, which have no particular interest on polynomials. Note
however that some specialised methods (for example p.rescale(a), equivalent to p(a*x)) are
often more efficient than more general methods that could replace them.
132 CHAP. 7. POLYNOMIALS

Change of Ring. The exact list of available operations, their meaning and
their efficiency heavily depend on the base ring. For example, the polynomials in
GF(p)[’x’] have a method small_roots which returns their small roots with
respect to the characteristic p; those in QQ[’x’] do not have such a method, since
it makes no sense. The factor method exists for all polynomials, but raises an
exception NotImplementedError for polynomials with coefficients in SR or in
Z/4Z. This exception means that this operation is not available in Sage for this
kind of object, despite having a mathematical meaning.
It is very useful to be able to juggle the different rings of coefficients on
which we might consider a given polynomial. Applied to a polynomial in A[x],
the method change_ring(B) returns its image in B[x], when a natural method
to convert the coefficients exists. The conversion is often given by a canonical
morphism from A to B: in particular, change_ring might be used to extend
the base ring to gain additional algebraic properties. Here for example, the
polynomial p is irreducible over the rationals, but it factors on R:
sage: x = polygen(QQ)
sage: p = x^2 - 16*x + 3
sage: p.factor()
x^2 - 16*x + 3
sage: p.change_ring(RDF).factor()
(x - 15.810249675906654) * (x - 0.18975032409334563)
The RDF domain is that of “machine floating-point numbers”, and is discussed in
Chapter 11. The obtained factorisation is approximate; it is not enough to recover
the original polynomial. To represent real roots of polynomials with integer
coefficients in a way that enables exact computations, we use the domain AA of
real algebraic numbers. We will see some examples in the following sections.
The same method change_ring allows to reduce a polynomial in Z[x] modulo
a prime number:
sage: p.change_ring(GF(3))
x^2 + 2*x
Conversely, if B ⊂ A and if the coefficients of p are in fact in B, we also call
change_ring to recover p in B[x].

Iteration. More generally, one often needs to apply a given transformation to


all coefficients of a polynomial. The method map_coefficients is designed for
this. Applied to a polynomial p ∈ A[x] with parameter a function f , it returns
the polynomial obtained by applying f to all non-zero coefficients of p. In general,
f is an anonymous function defined using the lambda construction (see §3.3.2).
Here is for example how one can compute the conjugate of a polynomial with
complex coefficients:
sage: QQi.<myI> = QQ[I] # myI is the i of QQi, I that of SR
sage: R.<x> = QQi[]; p = (x + 2*myI)^3; p
x^3 + 6*I*x^2 - 12*x - 8*I
sage: p.map_coefficients(lambda z: z.conjugate())
7.1. POLYNOMIAL RINGS 133

x^3 - 6*I*x^2 - 12*x + 8*I


Here, we can also write p.map_coefficients(conjugate), since conjugate(z)
has the same effect as z.conjugate for z ∈ Q[i]. Calling explicitly a method
of the object z is more robust: the code then works for all objects having a
conjugate() method, and only for those.

Operations on polynomial rings

The parents of polynomial objects, i.e., the rings A[x], are themselves
first class Sage objects. Let us briefly see how to use them.
A first family of methods enables us to construct particular polynomials,
to draw random ones, or to enumerate families, here those of degree exactly 2
over F2 :
sage: list(GF(2)['x'].polynomials(of_degree=2))
[x^2, x^2 + 1, x^2 + x, x^2 + x + 1]
We will call some of these methods in the examples of the next sections, to
build objects on which we will work. Chapter 15 explains more generally
how to enumerate finite sets with Sage.
Secondly, the system “knows” some basic facts for each polynomial ring.
We can check whether a given object is a ring, if it is noetherian:
sage: A = QQ['x']
sage: A.is_ring() and A.is_noetherian()
True
or if Z is a sub-ring of Q[x], and for which values of n the ring Z/nZ is
integral:
sage: ZZ.is_subring(A)
True
sage: [n for n in range(20)
....: if Integers(n)['x'].is_integral_domain()]
[0, 2, 3, 5, 7, 11, 13, 17, 19]
These capabilities largely rely on the Sage category system (see also §5.2.3).
Polynomial rings belong to a number of “categories”, like the category of
sets, that of Euclidean rings, and many more:
sage: R.categories()
[Category of euclidean domains,
Category of principal ideal domains,
...
Category of sets with partial maps, Category of objects]
This reflects that any polynomial ring is also a set, a Euclidean domain, and
so on. The system can thus automatically transfer to polynomial rings the
general properties of objects from these different categories.
134 CHAP. 7. POLYNOMIALS

Divisibility and Euclidean division


divisibility test p | q p.divides(q)
multiplicity of a divisor q k | p k = p.valuation(q)
Euclidean division p = qd + r q, r = p.quo_rem(d) or q = p//d, r = p%d
pseudo-division ak p = qd + r q, r, k = p.pseudo_divrem(d)
greatest common divisor p.gcd(q), gcd([p1, p2, p3])
least common multiple p.lcm(q), lcm([p1, p2, p3])
extended gcd g = up + vq g, u, v = p.xgcd(q) or xgcd(p, q)
“Chinese remainder” c ≡ a mod p, c = crt(a, b, p, q)
c ≡ b mod q

Miscellaneous
interpolation p(xi ) = yi p = R.lagrange_polynomial([(x1,y1), ...])
content of p ∈ Z[x] p.content()

Table 7.3 – Polynomial arithmetic.

7.2 Euclidean Arithmetic


Apart from the sum and product, the most elementary operations on polynomials
are the Euclidean division and the greatest common divisor computation. The
corresponding operators and methods (Table 7.3) mimic those on integers. How-
ever, quite often, these operations are hidden by an additional abstraction layer:
quotient of rings (§7.2.2) where each arithmetic operation involves an implicit
Euclidean division, rational functions (§7.4) whose normalisation implies some
gcd computations...

7.2.1 Divisibility
Divisions. The Euclidean division works in a field, and more generally in a
commutative ring when the leading coefficient of the divisor is invertible, since this
coefficient is the only one from the base ring by which it is required to divide:
sage: R.<t> = Integers(42)[]; (t^20-1) % (t^5+8*t+7)
22*t^4 + 14*t^3 + 14*t + 6
When the leading coefficient is not invertible, we can still define a pseudo Euclidean
division (pseudo-division for short): let A be a commutative ring, p, d ∈ A[x], and
a the leading coefficient of d. Then there exists two polynomials q, r ∈ A[x], with
deg r < deg d, and an integer k ≤ deg p − deg d + 1 such that

ak p = qd + r.

The pseudo-division is given by the pseudo_divrem method.


To perform an exact division, we also use the Euclidean quotient operator //.
Indeed, dividing by a non-constant polynomial with / returns a result of type
rational function (see §7.4), or fails when this makes no sense:
sage: ((t^2+t)//t).parent()
7.2. EUCLIDEAN ARITHMETIC 135

Univariate Polynomial Ring in t over Ring of integers modulo 42


sage: (t^2+t)/t
Traceback (most recent call last):
...
TypeError: self must be an integral domain.

Exercise 25. Usually, in Sage, polynomials in Q[x] are represented on the monomial
basis (xn )n∈N . Chebyshev polynomials Tn , defined by Tn (cos θ) = cos(nθ), form a family
of orthogonal polynomials and thus a basis of Q[x]. The first Chebyshev polynomials
are

sage: x = polygen(QQ); [chebyshev_T(n, x) for n in (0..4)]


[1, x, 2*x^2 - 1, 4*x^3 - 3*x, 8*x^4 - 8*x^2 + 1]

Write a function taking as input an element of Q[x] and returning the coefficients of its
decomposition in the basis (Tn )n∈N .

Exercise 26 (Division by increasing powers). Let n ∈ N and u, v ∈ A[x], with v(0)


invertible. Then a unique pair (q, r) of polynomials exists in A[x] with deg q ≤ n such
that u = qv + xn+1 r. Write a function which computes q and r by an analogue of the
Euclidean division algorithm. How would you perform this computation in the easiest
way, using available Sage functions?

GCD. Sage is able to compute the gcd of polynomials over a field, thanks
to the Euclidean structure of K[x], but also on some other rings, including the
integers:
sage: S.<x> = ZZ[]; p = 2*(x^10-1)*(x^8-1)
sage: p.gcd(p.derivative())
2*x^2 - 2

We can prefer the more symmetric expression gcd(p,q), which yields the same
result as p.gcd(q). It is though slightly less natural in Sage since it is not a
general mechanism: gcd(p,q) calls a function of two arguments, defined manually
in the source code of Sage, and which calls in turn p.gcd. Only some usual
methods have such an associated function.
The extended gcd, i.e., the computation of a Bézout relation

g = gcd(p, q) = ap + bq, g, p, q, a, b ∈ K[x]

is given by p.xgcd(q):
sage: R.<x> = QQ[]; p = x^5-1; q = x^3-1
sage: print("the gcd is %s = (%s)*p + (%s)*q" % p.xgcd(q))
the gcd is x - 1 = (-x)*p + (x^3 + 1)*q

The xgcd method also exists for polynomials in ZZ[’x’], but beware: since Z[x]
is not a principal ideal ring, the result is in general not a Bézout relation (ap + bq
might be an integer multiple of the gcd)!
136 CHAP. 7. POLYNOMIALS

7.2.2 Ideals and Quotients


Ideals of A[x]. The ideals of polynomial rings, and the quotients by these ideals,
are represented by Sage objects built from the polynomial ring by the methods
ideal and quo. The product of a tuple of polynomials by a polynomial ring is
interpreted as an ideal:

sage: R.<x> = QQ[]


sage: J1 = (x^2 - 2*x + 1, 2*x^2 + x - 3)*R; J1
Principal ideal (x - 1) of Univariate Polynomial Ring in x
over Rational Field

We can multiply ideals, and reduce a polynomial modulo an ideal:

sage: J2 = R.ideal(x^5 + 2)
sage: ((3*x+5)*J1*J2).reduce(x^10)
421/81*x^6 - 502/81*x^5 + 842/81*x - 680/81

The reduced polynomial remains in this case an element of QQ[’x’]. Another


way is to construct the quotient by an ideal and project the elements on it. The
parent of the projected element is then in the quotient ring. The lift method of
the quotient elements converts them back into the initial ring.

sage: B = R.quo((3*x+5)*J1*J2) # quo automatically names 'xbar' which is


sage: B(x^10) # the generator of B image of x
421/81*xbar^6 - 502/81*xbar^5 + 842/81*xbar - 680/81
sage: B(x^10).lift()
421/81*x^6 - 502/81*x^5 + 842/81*x - 680/81

If K is a field, then the ring K[x] is principal: the ideals are represented
during computations by a generator, all this being an algebraic language for the
operations seen in §7.2.1. Its principal advantage is that quotient
 rings can be
easily used in new constructions, here that of F5 [t]/ht2 + 3i [x]:

sage: R.<t> = GF(5)[]; R.quo(t^2+3)['x'].random_element()


(3*tbar + 1)*x^2 + (2*tbar + 3)*x + 3*tbar + 4

Sage also allows building non principal ideals like in Z[x], however the available
operations are then limited — except in case of multivariate polynomials over a
field, which are the subject of Chapter 9.
Exercise 27. We define the sequence (un )n∈N with the initial conditions un = n + 7
for 0 ≤ n < 1000, and the linear recurrence relation

un+1000 = 23un+729 − 5un+2 + 12un+1 + 7un (n ≥ 0).

Compute the last five digits of u1010000 . Hint: we might look at the algorithm from §3.2.4.
However, this algorithm is too expensive when the order of the recurrence is large.
Introduce a clever quotient of polynomial rings to avoid this issue.
7.3. FACTORISATION AND ROOTS 137

Construction of ideals and quotient rings Q = R/J


ideal hu, v, wi R.ideal(u, v, w) or (u, v, w)*R
reduction of p modulo J J.reduce(p) or p.mod(J)
quotient ring R/J, R/hpi R.quo(J), R.quo(p)
ring whose quotient gave Q Q.cover_ring()
isomorphic number field Q.number_field()

Elements of K[x]/hpi
lift (section of R ։ R/J) u.lift()
minimal polynomial u.minpoly()
characteristic polynomial u.charpoly()
matrix u.matrix()
trace u.trace()

Table 7.4 – Ideals and quotients.

Algebraic Extensions. An important special case is the quotient of K[x] by


an irreducible polynomial to build an algebraic extension of K. The number fields,
finite extensions of Q, are represented by the objects NumberField, distinct from
the quotients of QQ[’x’]. When this makes sense, the method number_field of a
quotient of polynomial rings returns the corresponding number field. The interface
of number fields, more complete than that of quotient rings, is beyond the scope
of this book. The non-prime finite fields Fpk , built as algebraic extensions of the
prime finite fields Fp , are described in §6.1.

7.3 Factorisation and Roots


A third level after the elementary operations and the Euclidean arithmetic
concerns the decomposition of a polynomial into a product of irreducible factors,
or factorisation. It is maybe where computer algebra is the most useful!

7.3.1 Factorisation
Irreducibility Test. On the algebraic side, the simplest question about the
factorisation of a polynomial is whether it is irreducible. Naturally, the answer
depends on the base ring. The method is_irreducible tells if a polynomial is
irreducible in its parent ring. For example, the polynomial 3x2 − 6 is irreducible
over Q, but not over Z (why?):
sage: R.<x> = QQ[]; p = 3*x^2 - 6
sage: p.is_irreducible(), p.change_ring(ZZ).is_irreducible()
(True, False)

Factorisation. The factorisation of an integer of hundreds or thousands of


digits is a very hard problem. In contrast, factoring a polynomial of degree 1000
138 CHAP. 7. POLYNOMIALS

on Q or Fp — for small p — needs only a few seconds3 :


sage: p = QQ['x'].random_element(degree=1000)
sage: %timeit p.factor()
1 loop, best of 3: 2.45 s per loop
Here ends the algorithmic similarity between polynomials and integers we have
seen in preceding sections.
Like the irreducibility test, the factorisation is performed on the base ring. For
example, the factorisation of a polynomial over the integers contains a constant
part, itself split into prime factors, and a product of primitive polynomials, i.e.,
whose coefficients are coprime:
sage: x = polygen(ZZ); p = 54*x^4+36*x^3-102*x^2-72*x-12
sage: p.factor()
2 * 3 * (3*x + 1)^2 * (x^2 - 2)
Sage is able to factor polynomials on various rings — rational, complex (approxi-
mate), finite fields and number fields in particular:
sage: for A in [QQ, ComplexField(16), GF(5), QQ[sqrt(2)]]:
....: print(str(A) + ":")
....: print(A['x'](p).factor())
Rational Field:
(54) * (x + 1/3)^2 * (x^2 - 2)
Complex Field with 16 bits of precision:
(54.00) * (x - 1.414) * (x + 0.3333)^2 * (x + 1.414)
Finite Field of size 5:
(4) * (x + 2)^2 * (x^2 + 3)
Number Field in sqrt2 with defining polynomial x^2 - 2:
(54) * (x - sqrt2) * (x + sqrt2) * (x + 1/3)^2
The result of a decomposition into irreducible factors is not a polynomial (since
the polynomials are always in normal form, i.e., in expanded form!), but an
object f of type Factorization. We obtain the ith factor with f[i], and we get
back the polynomial with f.expand(). The Factorization objects also provide
methods like gcd and lcm which have the same meaning as for polynomials, but
work on the factored forms.

Square-Free Decomposition. Despite its good theoretical and practical com-


plexity, the full factorisation of a polynomial is an expensive operation. The
square-free decomposition is a weaker factorisation, much easier to obtain — some
gcd computations
Qr are enough — and which already brings a lot of information.
Let p = i=1 pimi ∈ K[x] be a polynomial that splits into a product of
irreducible factors over a field K of characteristic zero. We say that p is square-
free if all its factors pi have multiplicity mi = 1, i.e., if the roots of p in an
3 On the theoretical side, we know how to factor in Q[x] in polynomial time, and in F [x]
p
in probabilistic polynomial time, whereas we do not know whether integers can be factored in
polynomial time.
7.3. FACTORISATION AND ROOTS 139

Factorisation
irreducibility test p.is_irreducible()
factorisation p.factor()
square-free factorisation p.squarefree_decomposition()
square-free part p/ gcd(p, p′ ) p.radical()

Roots
roots in A, in D p.roots(), p.roots(D)
real roots p.roots(RR), p.real_roots()
complex roots p.roots(CC), p.complex_roots()
isolation of real roots p.roots(RIF), p.real_root_intervals()
isolation of complex roots p.roots(CIF)
resultant p.resultant(q)
discriminant p.discriminant()
Galois group (p irreducible) p.galois_group()

Table 7.5 – Factorisation and roots.

algebraic closure of K are simple. A square-free decomposition is a factorisation


into a product of square-free and coprime factors:
Y
p = f1 f22 . . . fss where fm = pi .
mi =m

Hence, the square-free decomposition splits the irreducible factors of p by multi-


plicity. The square-free part f1 . . . fs = p1 . . . pr of p is the polynomial with simple
roots which has the same roots as p, disregarding multiplicities.

7.3.2 Root Finding


The computation of the roots of a polynomial may be performed in several ways:
Do we want real or complex roots? Roots in another domain? Do we want
exact or approximate roots? With or without multiplicities? In a guaranteed or
heuristic way? The roots method of a polynomial returns by default the roots in
its base ring, in the form of a list of pairs (root, multiplicity):
sage: R.<x> = ZZ[]; p = (2*x^2-5*x+2)^2 * (x^4-7); p.roots()
[(2, 2)]
With a parameter, roots(D) returns the roots in the domain D, here the rational
roots, and approximations of the ℓ-adic roots for ℓ = 19:
sage: p.roots(QQ)
[(2, 2), (1/2, 2)]
sage: p.roots(Zp(19, print_max_terms=3))
[(7 + 16*19 + 17*19^2 + ... + O(19^20), 1),
(12 + 2*19 + 19^2 + ... + O(19^20), 1),
(10 + 9*19 + 9*19^2 + ... + O(19^20), 2),
(2 + O(19^20), 2)]
140 CHAP. 7. POLYNOMIALS

This works for a large number of domains, with more or less efficiency.
In particular, selecting for D the field of algebraic numbers QQbar or that
of real algebraic numbers AA enables us to compute exactly the complex or real
roots of a polynomial with rational coefficients:

sage: roots = p.roots(AA); roots


[(-1.626576561697786?, 1), (0.500000000000000?, 2),
(1.626576561697786?, 1), (2.000000000000000?, 2)]

Sage plays transparently for the user with different representations of algebraic
numbers. One encodes each α ∈ Q̄ by its minimal polynomial together with a
sufficiently accurate interval to distinguish α from the other roots. Therefore,
despite their output, the returned roots are not just approximate values. They
can be reused in exact computations:

sage: a = roots[0][0]^4; a.simplify(); a


7

Here, we have raised the first root found to the fourth power, then forced Sage to
simplify the result to make it clear it equals the integer 7.
A variant of the exact resolution is to simply isolate the roots, i.e., determine
intervals containing exactly one root each, by giving as domain D that of the
real intervals RIF or complex intervals CIF. Among the other useful domains in
the case of a polynomial with rational coefficients, let us mention RR, CC, RDF,
CDF, which all correspond to approximate numerical roots, and the number fields
QQ[alpha].The specific methods real_roots, complex_roots and (for some base
rings) real_root_intervals offer additional options or give slightly different
results from the roots method. The numerical approximation and isolation of
roots is discussed in more detail in §12.2.

7.3.3 Resultant

In a unique factorisation domain, the existence of a common non-constant factor


between two polynomials is characterised by the nullity of their resultant Res(p, q),
which is a polynomial in their coefficients. A major advantage of the resultant
compared to the gcd is that it specialises well under ring morphisms. For example,
the polynomials x − 12 and x − 20 are coprime in Z[x], but the nullity of their
resultant

sage: x = polygen(ZZ); (x-12).resultant(x-20)


-8

modulo n shows that they have a common factor in Z/nZ if and only if n divides 8.
Pm Pn
Let p = i=0 pi xi and q = i=0 qi xi be two non constant polynomials in
7.3. FACTORISATION AND ROOTS 141

A[x], with pm , qn 6= 0. The resultant of p and q is defined by

pm ··· ··· p0
.. ..
. .
pm · · · · · · p0
Res(p, q) = qn ··· q0 . (7.1)
.. ..
. .
.. ..
. .
qn ··· q0

It is the determinant, in suitable bases, of the linear map

An−1 [x] × Am−1 [x] → Am+n−1 [x]


u, v 7→ up + vq

where Ak [x] ⊂ A[x] is the sub-module of polynomials of degree at most k. If p


and q split into linear factors, their resultant may also be expressed in terms of
differences of their roots:
Y 
p = pm (x − α1 ) . . . (x − αm )
Res(p, q) = pnm qnm (αi − βj ),
q = qn (x − β1 ) . . . (x − βn ).
i,j

The specialisation property mentioned above follows from the definition (7.1):
if ϕ : A → A′ is a ring morphism, the application of which to p and q keeps their
degrees unchanged, i.e., such that ϕ(pm ) 6= 0 and ϕ(qn ) 6= 0, then we have

Res(ϕ(p), ϕ(q)) = ϕ(Res(p, q)).

As a consequence, ϕ(Res(p, q)) vanishes when ϕ(p) and ϕ(q) share a common
factor. We have seen above an example of this phenomenon, with ϕ the canonical
projection from Z to Z/nZ.
The most common usage of the resultant concerns the case where the base
ring itself is a polynomial ring: p, q ∈ A[x] with A = K[a1 , . . . , ak ]. In particular,
given α1 , . . . , αk ∈ K, let us consider the specialisation

ϕ : B[a1 , . . . , ak ] → K
q(a1 , . . . , ak ) 7→ q(α1 , . . . , αk ).

We see that the resultant Res(p, q) vanishes at (α1 , . . . , αk ) if and only if the
specialisations ϕ(p), ϕ(q) ∈ K[x] share a common factor, assuming that one of
the leading terms of p and q does not vanish in (α1 , . . . , αk ).
For example, the discriminant of p ∈ Q[x] of degree m is defined by

disc(p) = (−1)m(m−1)/2 Res(p, p′ )/pm .

This definition generalises the classical discriminants of degree two and three
polynomials:
142 CHAP. 7. POLYNOMIALS

sage: R.<a,b,c,d> = QQ[]; x = polygen(R); p = a*x^2+b*x+c


sage: p.resultant(p.derivative())
-a*b^2 + 4*a^2*c
sage: p.discriminant()
b^2 - 4*a*c
sage: (a*x^3 + b*x^2 + c*x + d).discriminant()
b^2*c^2 - 4*a*c^3 - 4*b^3*d + 18*a*b*c*d - 27*a^2*d^2
Since the discriminant of p is, up to a normalisation, the resultant of p and its
derivative, it vanishes if and only if p has a multiple root in C.

7.3.4 Galois Group


The Galois group of an irreducible polynomial p ∈ Q[x] is an algebraic object
which describes some of the “symmetries” of the roots of p. It is a central object in
the theory of algebraic equations. In particular, the equation p(x) = 0 is solvable
by radicals — i.e., its roots can be expressed from coefficients of p using the four
operations and the nth root — if and only if the Galois group of p is solvable.
Sage allows the computation of the Galois group of polynomials with rational
coefficients of moderate degree, and performs several operations on the obtained
groups. Both Galois theory and the group theory functionalities of Sage go beyond
the scope of this book. Let us simply apply without more explanations Galois’
theorem on the solvability by radicals. The following computation4 shows that
the roots of x5 − x − 1 cannot be expressed using radicals:
sage: x = polygen(QQ); G = (x^5 - x - 1).galois_group(); G
Transitive group number 5 of degree 5
sage: G.is_solvable()
False
It is one of the simplest examples of this situation, since polynomials of degree
less than or equal to 4 are always solvable by radicals, as well as obviously those
of the form x5 − a. By looking at the generators of G seen as a permutation
group, we recognise that G ≃ S5 , which can be easily verified:
sage: G.gens()
[(1,2,3,4,5), (1,2)]
sage: G.is_isomorphic(SymmetricGroup(5))
True

7.4 Rational Functions


7.4.1 Construction and Basic Properties
The division of two polynomials (on an integral ring) produces a rational function.
Its parent is the fraction field of the polynomial ring, obtained with Frac(R):
4 This computation requires a table of finite groups which is not in the default installation of

Sage, but we can upload and automatically install it with the command sage -i database_gap
(it might be needed to restart Sage after the installation).
7.4. RATIONAL FUNCTIONS 143

Rational functions
fraction field K(x) Frac(K['x'])
numerator r.numerator()
denominator r.denominator()
simplification (modifies r) r.reduce()
partial fraction decomposition r.partial_fraction_decomposition()
rational reconstruction of s mod m s.rational_reconstruct(m)

Truncated power series


ring A[[t]] PowerSeriesRing(A, 'x', default_prec=n)
ring A((t)) LaurentSeriesRing(A, 'x', default_prec=n)
coefficient [xk ] f (x) f[k]
truncation x + O(x^n)
precision f.prec()
derivative, antiderivative (vanishes
p at 0) f.derivative(), f.integral()
usual operations f , exp f , ... f.sqrt(), f.exp(), ...
reciprocal (f ◦ g = g ◦ f = x) g = f.reverse()
solution of y ′ = ay + b a.solve_linear_de(precision, b)

Table 7.6 – Objects constructed from polynomials.

sage: x = polygen(RR); r = (1 + x)/(1 - x^2); r.parent()


Fraction Field of Univariate Polynomial Ring in x over Real
Field with 53 bits of precision
sage: r
(x + 1.00000000000000)/(-x^2 + 1.00000000000000)
We see that the simplification is not automatic. This is because RR is an inexact
ring, i.e., its elements are approximations of mathematical objects. The reduce
method puts the fraction in reduced form. It does not return a new object, but
modifies the existing fraction:
sage: r.reduce(); r
1.00000000000000/(-x + 1.00000000000000)
On an exact ring, in contrast, rational functions are automatically reduced.
The operations on rational functions are analogous to those on polynomials.
Those having a meaning in both cases (substitution, derivative, factorisation...)
may be used in the same manner. Table 7.6 enumerates some other useful methods.
The partial fraction decomposition and the rational reconstruction deserve some
explanations.

7.4.2 Partial Fraction Decomposition


Sage computes the partial fraction decomposition of a rational function a/b in
Frac(K[’x’]) from the factorisation of b in K[’x’]. It is therefore the partial
fraction decomposition on K. The result contains a polynomial part p and a list
of rational functions whose denominators are powers of irreducible factors of b:
144 CHAP. 7. POLYNOMIALS

sage: R.<x> = QQ[]; r = x^10 / ((x^2-1)^2 * (x^2+3))


sage: poly, parts = r.partial_fraction_decomposition()
sage: poly
x^4 - x^2 + 6
sage: for part in parts: part.factor()
(17/32) * (x - 1)^-1
(1/16) * (x - 1)^-2
(-17/32) * (x + 1)^-1
(1/16) * (x + 1)^-2
(-243/16) * (x^2 + 3)^-1
We have thus obtained the partial fraction decomposition on the rationals
17 1 17 1 243
x10
r= = x4 −x2 +6+ 32 + 16 2 − 32 + 16 2 − 216 .
(x2 − 1) (x + 3)
2 2 x − 1 (x − 1) x + 1 (x + 1) x + 3
This is also clearly the partial fraction decomposition of r on the real numbers.
However, on the complex numbers, the denominator of the last term is
not irreducible, hence the rational function can be further decomposed. We can
compute the partial fraction decomposition on the complex numbers numerically:
sage: C = ComplexField(15)
sage: Frac(C['x'])(r).partial_fraction_decomposition()
(x^4 - x^2 + 6.000, [0.5312/(x - 1.000), 0.06250/(x^2 - 2.000*x + 1.000)
,
4.385*I/(x - 1.732*I), (-4.385*I)/(x + 1.732*I),
(-0.5312)/(x + 1.000), 0.06250/(x^2 + 2.000*x + 1.000)])
We obtain the exact decomposition on C in the same manner, by replacing C by
QQbar. Doing the computation on AA, we would get the decomposition on the
reals, even when all real roots of the denominator are not rational.

7.4.3 Rational Reconstruction


As for integers in §6.1.3, the rational reconstruction also exists for polynomials
with coefficients in A = Z/nZ. Given m, s ∈ A[x], the command
sage: s.rational_reconstruct(m, dp, dq)
computes when possible polynomials p, q ∈ A[x] such that
qs ≡ p mod m, deg p ≤ dp , deg q ≤ dq .
For simplicity, let us restrict ourselves to the case where n is prime. Such a
relation with q and m coprime implies p/q = s in A[x]/hmi, which explains the
“rational reconstruction” name.
The rational reconstruction problem translates into a linear system on the
coefficients of p and q, and a simple dimension argument shows that a non-
trivial solution exists as soon as dp + dq ≥ deg m − 1. A solution with q and
m coprime does not always exist (for example, the solutions of p ≡ qx mod x2
with deg p ≤ 0, deg q ≤ 1 are the constant multiples of (p, q) = (0, x)), but
rational_reconstruct looks rather for solutions q coprime to m.
7.4. RATIONAL FUNCTIONS 145

Padé Approximants. The case m = xn is called Padé approximant. A Padé


approximant of type (k, n − k) of a formal power series f ∈ K[[x]] is a rational
function p/q ∈ K(x) such that deg p ≤ k − 1, deg q ≤ n − k, q(0) = 1, and
p/q = f + O(xn ). We then have p/q ≡ f mod xn .
Let us start with a symbolic example.
P∞ The following commands compute a
Padé approximant of the series f = i=0 (i + 1)2 xi with coefficients in Z/101Z:
sage: A = Integers(101); R.<x> = A[]
sage: f6 = sum( (i+1)^2 * x^i for i in (0..5) ); f6
36*x^5 + 25*x^4 + 16*x^3 + 9*x^2 + 4*x + 1
sage: num, den = f6.rational_reconstruct(x^6, 1, 3); num/den
(100*x + 100)/(x^3 + 98*x^2 + 3*x + 100)
By expanding back into power series the rational function found, we see that
not only the terms correspond up the term in x5 , but even the next term “is
correct”!
sage: S = PowerSeriesRing(A, 'x', 7); S(num)/S(den)
1 + 4*x + 9*x^2 + 16*x^3 + 25*x^4 + 36*x^5 + 49*x^6 + O(x^7)
Indeed, f itself is a rational function: we have f = (1 + x)/(1 − x)3 . The
truncated expansion f6, together with bounds on the degrees of the numerator
and denominator, is enough to represent it without any ambiguity. From this
point of view, the computation of Padé approximants is the converse of the
series expansion of power series: it allows us to go back from this alternative
representation to the usual one as quotient of two polynomials.

An Analytic Example. Historically, Padé approximants do not come from


this kind of symbolic reasoning, but from the approximation theory of analytic
functions. Indeed, the Padé approximants of the series expansion of an analytic
function often approximate the function better than series truncations. When
the degree of the denominator is large enough, Padé approximants can even give
good approximations outside the convergence disc of the series. We sometimes
say that they “swallow the poles”. Figure 7.1, which shows the convergence of
the approximants of type (2k, k) of the tangent function around 0, illustrates this
phenomenon.
Although rational_reconstruct is restricted to polynomials on Z/nZ, it is
possible to use it to compute Padé approximants with rational coefficients, and
obtain that figure. The simplest way is to first perform the rational reconstruction
modulo a large enough prime:
sage: x = var('x'); s = tan(x).taylor(x, 0, 20)
sage: p = previous_prime(2^30); ZpZx = Integers(p)['x']
sage: Qx = QQ['x']

sage: num, den = ZpZx(s).rational_reconstruct(ZpZx(x)^10,4,5)


sage: num/den
(1073741779*x^3 + 105*x)/(x^4 + 1073741744*x^2 + 105)
146 CHAP. 7. POLYNOMIALS

-6 -4 -2 2 4 6 type (4, 2)
-1 ···········
type (8, 4)
-2 ·− ·− ·−
type (12, 6)
-3
−−−−−

Figure 7.1 – The tangent function and some Padé approximants on [−2π, 2π].

then to lift the solution found. The following function lifts an element a from
Z/pZ into an integer of absolute value at most p/2.
sage: def lift_sym(a):
....: m = a.parent().defining_ideal().gen()
....: n = a.lift()
....: if n <= m // 2: return n
....: else: return n - m
We then get:
sage: Qx(map(lift_sym, num))/Qx(map(lift_sym, den))
(-10*x^3 + 105*x)/(x^4 - 45*x^2 + 105)
When the wanted coefficients are too large for this technique, we can perform the
computation modulo several primes, and apply the “Chinese Remainder Theorem”
to obtain a solution with integer coefficients, as explained in §6.1.4. Another
possibility is to compute a recurrence relation with constant coefficients which
is satisfied by the series coefficients. This computation is almost equivalent to a
Padé approximant (see Exercise 28), but the Sage function berlekamp_massey is
able to perform it on any field.
Let us make the preceding computation more automatic, by writing a func-
tion which directly computes the approximant with rational coefficients, under
favorable assumptions:
sage: def mypade(pol, n, k):
....: x = ZpZx.gen();
....: n,d = ZpZx(pol).rational_reconstruct(x^n, k-1, n-k)
....: return Qx(map(lift_sym, n))/Qx(map(lift_sym, d))
It then suffices to call plot on the results of this function (converted into elements
of SR, since plot is not able to draw directly the graph of an “algebraic” rational
function) to obtain the graph of Figure 7.1:

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy