├── .github ├── pull_request_template.md └── workflows │ ├── update_versions.yml │ └── upgrade.yml ├── .gitignore ├── .gitpod.yml ├── LICENSE ├── README.md ├── leanpkg.toml ├── mk_exercises.py └── src ├── exercises ├── 00_first_proofs.lean ├── 01_equality_rewriting.lean ├── 02_iff_if_and.lean ├── 03_forall_or.lean ├── 04_exists.lean ├── 05_sequence_limits.lean ├── 06_sub_sequences.lean ├── 07_first_negations.lean ├── 07bis_abstract_negations.lean ├── 08_limits_negation.lean └── 09_limits_final.lean └── solutions ├── 00_first_proofs.lean ├── 01_equality_rewriting.lean ├── 02_iff_if_and.lean ├── 03_forall_or.lean ├── 04_exists.lean ├── 05_sequence_limits.lean ├── 06_sub_sequences.lean ├── 07_first_negations.lean ├── 07bis_abstract_negations.lean ├── 08_limits_negation.lean ├── 09_limits_final.lean └── tuto_lib.lean /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Please make sure you modified the files in `src/solutions` and used the `mk_exercises.py` script to recreate the files in `src/exercises`. Otherwise your modifications will soon be automatically overwritten. 2 | -------------------------------------------------------------------------------- /.github/workflows/update_versions.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | 6 | jobs: 7 | update_lean_xyz_branch: 8 | runs-on: ubuntu-latest 9 | name: Update lean-x.y.z branch 10 | steps: 11 | 12 | - name: checkout project 13 | uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: update branch 18 | uses: leanprover-contrib/update-versions-action@master -------------------------------------------------------------------------------- /.github/workflows/upgrade.yml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | - cron: '0 0 * * 1' 4 | 5 | jobs: 6 | upgrade_lean: 7 | runs-on: ubuntu-latest 8 | name: Bump Lean and dependency versions 9 | steps: 10 | - name: checkout project 11 | uses: actions/checkout@v2 12 | - name: upgrade Lean and dependencies 13 | uses: leanprover-contrib/lean-upgrade-action@master 14 | with: 15 | repo: ${{ github.repository }} 16 | access-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.olean 2 | /_target 3 | /leanpkg.path 4 | my_exercises 5 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: leanprovercommunity/mathlib:gitpod 2 | 3 | vscode: 4 | extensions: 5 | - jroesch.lean 6 | 7 | tasks: 8 | - command: . /home/gitpod/.profile && leanpkg configure && leanproject get-mathlib-cache 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lean-tutorials 2 | 3 | **Warning: This repository is deprecated. It uses an obsolete version of Lean.** 4 | 5 | The Lean 4 version can be found at https://github.com/leanprover-community/tutorials4. Many more learning resources are listed at https://leanprover-community.github.io/learn.html. 6 | -------------------------------------------------------------------------------- /leanpkg.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tuto" 3 | version = "0.1" 4 | lean_version = "leanprover-community/lean:3.49.1" 5 | path = "src/solutions" 6 | 7 | [dependencies] 8 | mathlib = {git = "https://github.com/leanprover-community/mathlib", rev = "d4f69d96f3532729da8ebb763f4bc26fcf640f06"} 9 | -------------------------------------------------------------------------------- /mk_exercises.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | from pathlib import Path 5 | 6 | sorry_regex = re.compile(r'(.*)-- sorry.*') 7 | root = Path(__file__).parent/'src' 8 | 9 | if __name__ == '__main__': 10 | for path in (root/'solutions').glob('**/*.lean'): 11 | if path.name == 'tuto_lib.lean': 12 | continue 13 | print(path) 14 | out = root/'exercises'/path.relative_to(root/'solutions') 15 | with out.open('w') as outp: 16 | with path.open() as inp: 17 | state = 'normal' 18 | for line in inp: 19 | m = sorry_regex.match(line) 20 | if state == 'normal': 21 | if m: 22 | state = 'sorry' 23 | else: 24 | outp.write(line) 25 | else: 26 | if m: 27 | state = 'normal' 28 | outp.write(m.group(1)+ 'sorry\n') 29 | 30 | outp.write('\n') 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/exercises/00_first_proofs.lean: -------------------------------------------------------------------------------- 1 | /- 2 | This file is intended for Lean beginners. The goal is to demonstrate what it feels like to prove 3 | things using Lean and mathlib. Complicated definitions and theory building are not covered. 4 | Everything is covered again more slowly and with exercises in the next files. 5 | -/ 6 | 7 | -- We want real numbers and their basic properties 8 | import data.real.basic 9 | 10 | -- We want to be able to use Lean's built-in "help" functionality 11 | import tactic.suggest 12 | 13 | -- We want to be able to define functions using the law of excluded middle 14 | noncomputable theory 15 | open_locale classical 16 | 17 | 18 | /- 19 | Our first goal is to define the set of upper bounds of a set of real numbers. 20 | This is already defined in mathlib (in a more general context), but we repeat 21 | it for the sake of exposition. Right-click "upper_bounds" below to get offered 22 | to jump to mathlib's version 23 | -/ 24 | #check upper_bounds 25 | 26 | /-- The set of upper bounds of a set of real numbers ℝ -/ 27 | def up_bounds (A : set ℝ) := { x : ℝ | ∀ a ∈ A, a ≤ x} 28 | 29 | /-- Predicate `is_maximum a A` means `a` is a maximum of `A` -/ 30 | def is_maximum (a : ℝ) (A : set ℝ) := a ∈ A ∧ a ∈ up_bounds A 31 | 32 | /- 33 | In the above definition, the symbol `∧` means "and". We also see the most 34 | visible difference between set theoretic foundations and type theoretic ones 35 | (used by almost all proof assistants). In set theory, everything is a set, and the 36 | only relation you get from foundations are `=` and `∈`. In type theory, there is 37 | a meta-theoretic relation of "typing": `a : ℝ` reads "`a` is a real number" or, 38 | more precisely, "the type of `a` is `ℝ`". Here "meta-theoretic" means this is not a 39 | statement you can prove or disprove inside the theory, it's a fact that is true or 40 | not. Here we impose this fact, in other circumstances, it would be checked by the 41 | Lean kernel. 42 | By contrast, `a ∈ A` is a statement inside the theory. Here it's part of the 43 | definition, in other circumstances it could be something proven inside Lean. 44 | -/ 45 | 46 | /- For illustrative purposes, we now define an infix version of the above predicate. 47 | It will allow us to write `a is_a_max_of A`, which is closer to a sentence. 48 | -/ 49 | infix ` is_a_max_of `:55 := is_maximum 50 | 51 | /- 52 | Let's prove something now! A set of real numbers has at most one maximum. Here 53 | everything left of the final `:` is introducing the objects and assumption. The equality 54 | `x = y` right of the colon is the conclusion. 55 | -/ 56 | lemma unique_max (A : set ℝ) (x y : ℝ) (hx : x is_a_max_of A) (hy : y is_a_max_of A) : x = y := 57 | begin 58 | -- We first break our assumptions in their two constituent pieces. 59 | -- We are free to choose the name following `with` 60 | cases hx with x_in x_up, 61 | cases hy with y_in y_up, 62 | -- Assumption `x_up` means x isn't less than elements of A, let's apply this to y 63 | specialize x_up y, 64 | -- Assumption `x_up` now needs the information that `y` is indeed in `A`. 65 | specialize x_up y_in, 66 | -- Let's do this quicker with roles swapped 67 | specialize y_up x x_in, 68 | -- We explained to Lean the idea of this proof. 69 | -- Now we know `x ≤ y` and `y ≤ x`, and Lean shouldn't need more help. 70 | -- `linarith` proves equalities and inequalities that follow linearly from 71 | -- the assumption we have. 72 | linarith, 73 | end 74 | 75 | /- 76 | The above proof is too long, even if you remove comments. We don't really need the 77 | unpacking steps at the beginning; we can access both parts of the assumption 78 | `hx : x is_a_max_of A` using shortcuts `hx.1` and `hx.2`. We can also improve 79 | readability without assistance from the tactic state display, clearly announcing 80 | intermediate goals using `have`. This way we get to the following version of the 81 | same proof. 82 | -/ 83 | 84 | example (A : set ℝ) (x y : ℝ) (hx : x is_a_max_of A) (hy : y is_a_max_of A) : x = y := 85 | begin 86 | have : x ≤ y, from hy.2 x hx.1, 87 | have : y ≤ x, from hx.2 y hy.1, 88 | linarith, 89 | end 90 | 91 | /- 92 | Notice how mathematics based on type theory treats the assumption 93 | `∀ a ∈ A, a ≤ y` as a function turning an element `a` of `A` into the statement 94 | `a ≤ y`. More precisely, this assumption is the abbreviation of 95 | `∀ a : ℝ, a ∈ A → a ≤ y`. The expression `hy.2 x` appearing in the above proof 96 | is then the statement `x ∈ A → x ≤ y`, which itself is a function turning a 97 | statement `x ∈ A` into `x ≤ y` so that the full expression `hy.2 x hx.1` is 98 | indeed a proof of `x ≤ y`. 99 | 100 | One could argue a three-line-long proof of this lemma is still two lines too long. 101 | This is debatable, but mathlib's style is to write very short proofs for trivial 102 | lemmas. Those proofs are not easy to read but they are meant to indicate that the 103 | proof is probably not worth reading. 104 | 105 | In order to reach this stage, we need to know what `linarith` did for us. It invoked 106 | the lemma `le_antisymm` which says: `x ≤ y → y ≤ x → x = y`. This arrow, which 107 | is used both for function and implication, is right associative. So the statement is 108 | `x ≤ y → (y ≤ x → x = y)` which reads: I will send a proof `p` of `x ≤ y` to a function 109 | sending a proof `q'` of `y ≤ x` to a proof of `x = y`. Hence `le_antisymm p q'` is a 110 | proof of `x = y`. 111 | 112 | Using this we can get our one-line proof: 113 | -/ 114 | 115 | example (A : set ℝ) (x y : ℝ) (hx : x is_a_max_of A) (hy : y is_a_max_of A) : x = y := 116 | le_antisymm (hy.2 x hx.1) (hx.2 y hy.1) 117 | 118 | /- 119 | Such a proof is called a proof term (or a "term mode" proof). Notice it has no `begin` 120 | and `end`. It is directly the kind of low level proof that the Lean kernel is 121 | consuming. Commands like `cases`, `specialize` or `linarith` are called tactics, they 122 | help users constructing proof terms that could be very tedious to write directly. 123 | The most efficient proof style combines tactics with proof terms like our previous 124 | `have : x ≤ y, from hy.2 x hx.1` where `hy.2 x hx.1` is a proof term embeded inside 125 | a tactic mode proof. 126 | 127 | In the remaining of this file, we'll be characterizing infima of sets of real numbers 128 | in term of sequences. 129 | -/ 130 | 131 | /-- The set of lower bounds of a set of real numbers ℝ -/ 132 | def low_bounds (A : set ℝ) := { x : ℝ | ∀ a ∈ A, x ≤ a} 133 | 134 | /- 135 | We now define `a` is an infimum of `A`. Again there is already a more general version 136 | in mathlib. 137 | -/ 138 | def is_inf (x : ℝ) (A : set ℝ) := x is_a_max_of (low_bounds A) 139 | infix ` is_an_inf_of `:55 := is_inf 140 | 141 | /- 142 | We need to prove that any number which is greater than the infimum of A is greater 143 | than some element of A. 144 | -/ 145 | 146 | lemma inf_lt {A : set ℝ} {x : ℝ} (hx : x is_an_inf_of A) : 147 | ∀ y, x < y → ∃ a ∈ A, a < y := 148 | begin 149 | -- Let `y` be any real number. 150 | intro y, 151 | -- Let's prove the contrapositive 152 | contrapose, 153 | -- The symbol `¬` means negation. Let's ask Lean to rewrite the goal without negation, 154 | -- pushing negation through quantifiers and inequalities 155 | push_neg, 156 | -- Let's assume the premise, calling the assumption `h` 157 | intro h, 158 | -- `h` is exactly saying `y` is a lower bound of `A` so the second part of 159 | -- the infimum assumption `hx` applied to `y` and `h` is exactly what we want. 160 | exact hx.2 y h 161 | end 162 | 163 | /- 164 | In the above proof, the sequence `contrapose, push_neg` is so common that it can be 165 | abbreviated to `contrapose!`. With these commands, we enter the gray zone between 166 | proof checking and proof finding. Practical computer proof checking crucially needs 167 | the computer to handle tedious proof steps. In the next proof, we'll start using 168 | `linarith` a bit more seriously, going one step further into automation. 169 | 170 | Our next real goal is to prove inequalities for limits of sequences. We extract the 171 | following lemma: if `y ≤ x + ε` for all positive `ε` then `y ≤ x`. 172 | -/ 173 | 174 | 175 | lemma le_of_le_add_eps {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) → y ≤ x := 176 | begin 177 | -- Let's prove the contrapositive, asking Lean to push negations right away. 178 | contrapose!, 179 | -- Assume `h : x < y`. 180 | intro h, 181 | -- We need to find `ε` such that `ε` is positive and `x + ε < y`. 182 | -- Let's use `(y-x)/2` 183 | use ((y-x)/2), 184 | -- we now have two properties to prove. Let's do both in turn, using `linarith` 185 | split, 186 | linarith, 187 | linarith, 188 | end 189 | 190 | /- 191 | Note how `linarith` was used for both sub-goals at the end of the above proof. 192 | We could have shortened that using the semi-colon combinator instead of comma, 193 | writing `split ; linarith`. 194 | 195 | Next we will study a compressed version of that proof: 196 | -/ 197 | 198 | example {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) → y ≤ x := 199 | begin 200 | contrapose!, 201 | exact assume h, ⟨(y-x)/2, by linarith, by linarith⟩, 202 | end 203 | 204 | /- 205 | The angle brackets `⟨` and `⟩` introduce compound data or proofs. A proof 206 | of a `∃ z, P z` statemement is composed of a witness `z₀` and a proof `h` of 207 | `P z₀`. The compound is denoted by `⟨z₀, h⟩`. In the example above, the predicate is 208 | itself compound, it is a conjunction `P z ∧ Q z`. So the proof term should read 209 | `⟨z₀, ⟨h₁, h₂⟩⟩` where `h₁` (resp. `h₂`) is a proof of `P z₀` (resp. `Q z₀`). 210 | But these so-called "anonymous constructor" brackets are right-associative, so we can 211 | get rid of the nested brackets. 212 | 213 | The keyword `by` introduces tactic mode inside term mode, it is a shorter version 214 | of the `begin`/`end` pair, which is more convenient for single tactic blocks. 215 | In this example, `begin` enters tactic mode, `exact` leaves it, `by` re-enters it. 216 | 217 | Going all the way to a proof term would make the proof much longer, because we 218 | crucially use automation with `contrapose!` and `linarith`. We can still get a one-line 219 | proof using curly braces to gather several tactic invocations, and the `by` abbreviation 220 | instead of `begin`/`end`: 221 | -/ 222 | 223 | example {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) → y ≤ x := 224 | by { contrapose!, exact assume h, ⟨(y-x)/2, by linarith, by linarith⟩ } 225 | 226 | /- 227 | One could argue that the above proof is a bit too terse, and we are relying too much 228 | on linarith. Let's have more `linarith` calls for smaller steps. For the sake 229 | of (tiny) variation, we will also assume the premise and argue by contradiction 230 | instead of contraposing. 231 | -/ 232 | 233 | example {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) → y ≤ x := 234 | begin 235 | intro h, 236 | -- Assume the conclusion is false, and call this assumption H. 237 | by_contradiction H, 238 | push_neg at H, 239 | -- Now let's compute. 240 | have key := calc 241 | -- Each line must end with a colon followed by a proof term 242 | -- We want to specialize our assumption `h` to `ε = (y-x)/2` but this is long to 243 | -- type, so let's put a hole `_` that Lean will fill in by comparing the 244 | -- statement we want to prove and our proof term with a hole. As usual, 245 | -- positivity of `(y-x)/2` is proved by `linarith` 246 | y ≤ x + (y-x)/2 : h _ (by linarith) 247 | ... = x/2 + y/2 : by ring 248 | ... < y : by linarith, 249 | -- our key now says `y < y` (notice how the sequence `≤`, `=`, `<` was correctly 250 | -- merged into a `<`). Let `linarith` find the desired contradiction now. 251 | linarith, 252 | -- alternatively, we could have provided the proof term 253 | -- `exact lt_irrefl y key` 254 | end 255 | 256 | /- 257 | Now we are ready for some analysis. Let's define convergence of sequences of real numbers 258 | (of course there is a much more general definition in mathlib). 259 | -/ 260 | 261 | /-- The sequence `u` tends to `l` -/ 262 | def limit (u : ℕ → ℝ) (l : ℝ) := ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε 263 | 264 | /- 265 | In the above definition, `u n` denotes the n-th term of the sequence. We can 266 | add parentheses to get `u(n)` but we try to avoid parentheses because they pile up 267 | very quickly 268 | -/ 269 | 270 | -- If y ≤ u n for all n and u n goes to x then y ≤ x 271 | lemma le_lim {x y : ℝ} {u : ℕ → ℝ} (hu : limit u x) (ineq : ∀ n, y ≤ u n) : y ≤ x := 272 | begin 273 | -- Let's apply our previous lemma 274 | apply le_of_le_add_eps, 275 | -- We need to prove y ≤ x + ε for all positive ε. 276 | -- Let ε be any positive real 277 | intros ε ε_pos, 278 | -- we now specialize our limit assumption to this `ε`, and immediately 279 | -- fix a `N` as promised by the definition. 280 | cases hu ε ε_pos with N HN, 281 | -- Now we only need to compute until reaching the conclusion 282 | calc 283 | y ≤ u N : ineq N 284 | ... = x + (u N - x) : by linarith 285 | -- We'll need `add_le_add` which says `a ≤ b` and `c ≤ d` implies `a + c ≤ b + d` 286 | -- We need a lemma saying `z ≤ |z|`. Because we don't know the name of this lemma, 287 | -- let's use `library_search`. Because searching through the library is slow, 288 | -- Lean will write what it found in the Lean message window when cursor is on 289 | -- that line, so that we can replace it by the lemma. We see `le_abs_self`, which 290 | -- says `a ≤ |a|`, exactly what we're looking for. 291 | ... ≤ x + |u N - x| : add_le_add (by linarith) (by library_search) 292 | ... ≤ x + ε : add_le_add (by linarith) (HN N (by linarith)), 293 | end 294 | 295 | /- 296 | The next lemma has been extracted from the main proof in order to discuss numbers. 297 | In ordinary maths, we know that ℕ is *not* contained in `ℝ`, whatever the 298 | construction of real numbers that we use. For instance a natural number is not 299 | an equivalence class of Cauchy sequences. But it's very easy to 300 | pretend otherwise. Formal maths requires slightly more care. In the statement below, 301 | the "type ascription" `(n + 1 : ℝ)` forces Lean to convert the natural number 302 | `n+1` into a real number. The "inclusion" map will be displayed in tactic state 303 | as `↑`. There are various lemmas asserting this map is compatible with addition and 304 | monotone, but we don't want to bother writing their names. The `norm_cast` 305 | tactic is designed to wisely apply those lemmas for us. 306 | -/ 307 | 308 | lemma inv_succ_pos : ∀ n : ℕ, 1/(n+1 : ℝ) > 0 := 309 | begin 310 | -- Let `n` be any integer 311 | intro n, 312 | -- Since we don't know the name of the relevant lemma, asserting that the inverse of 313 | -- a positive number is positive, let's state that is suffices 314 | -- to prove that `n+1`, seen as a real number, is positive, and ask `library_search` 315 | suffices : (n + 1 : ℝ) > 0, 316 | { library_search }, 317 | -- Now we want to reduce to a statement about natural numbers, not real numbers 318 | -- coming from natural numbers. 319 | norm_cast, 320 | -- and then get the usual help from `linarith` 321 | linarith, 322 | end 323 | 324 | /- 325 | That was a pretty long proof for an obvious fact. And stating it as a lemma feels 326 | stupid, so let's find a way to write it on one line in case we want to include it 327 | in some other proof without stating a lemma. First the `library_search` call 328 | above displays the name of the relevant lemma: `one_div_pos`. We can also 329 | replace the `linarith` call on the last line by `library_search` to learn the name 330 | of the lemma `nat.succ_pos` asserting that the successor of a natural number is 331 | positive. There is also a variant on `norm_cast` that combines it with `exact`. 332 | The term mode analogue of `intro` is `λ`. We get down to: 333 | -/ 334 | 335 | example : ∀ n : ℕ, 1/(n+1 : ℝ) > 0 := 336 | λ n, one_div_pos.mpr (by exact_mod_cast nat.succ_pos n) 337 | 338 | /- 339 | The next proof uses mostly known things, so we will commment only new aspects. 340 | -/ 341 | 342 | lemma limit_inv_succ : ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, 1/(n + 1 : ℝ) ≤ ε := 343 | begin 344 | intros ε ε_pos, 345 | suffices : ∃ N : ℕ, 1/ε ≤ N, 346 | { -- Because we didn't provide a name for the above statement, Lean called it `this`. 347 | -- Let's fix an `N` that works. 348 | cases this with N HN, 349 | use N, 350 | intros n Hn, 351 | -- Now we want to rewrite the goal using lemmas 352 | -- `div_le_iff' : 0 < b → (a / b ≤ c ↔ a ≤ b * c)` 353 | -- `div_le_iff : 0 < b → (a / b ≤ c ↔ a ≤ c * b)` 354 | -- the second one will be rewritten from right to left, as indicated by `←`. 355 | -- Lean will create a side goal for the required positivity assumption that 356 | -- we don't provide for `div_le_iff'`. 357 | rw [div_le_iff', ← div_le_iff ε_pos], 358 | -- We want to replace assumption `Hn` by its real counter-part so that 359 | -- linarith can find what it needs. 360 | replace Hn : (N : ℝ) ≤ n, exact_mod_cast Hn, 361 | linarith, 362 | -- we are still left with the positivity assumption, but already discussed 363 | -- how to prove it in the preceding lemma 364 | exact_mod_cast nat.succ_pos n }, 365 | -- Now we need to prove that sufficient statement. 366 | -- We want to use that `ℝ` is archimedean. So we start typing 367 | -- `exact archimedean_` and hit Ctrl-space to see what completion Lean proposes 368 | -- the lemma `archimedean_iff_nat_le` sounds promising. We select the left to 369 | -- right implication using `.1`. This a generic lemma for fields equiped with 370 | -- a linear (ie total) order. We need to provide a proof that `ℝ` is indeed 371 | -- archimedean. This is done using the `apply_instance` tactic that will be 372 | -- covered elsewhere. 373 | exact archimedean_iff_nat_le.1 (by apply_instance) (1/ε), 374 | end 375 | 376 | /- 377 | We can now put all pieces together, with almost no new things to explain. 378 | -/ 379 | 380 | lemma inf_seq (A : set ℝ) (x : ℝ) : 381 | (x is_an_inf_of A) ↔ (x ∈ low_bounds A ∧ ∃ u : ℕ → ℝ, limit u x ∧ ∀ n, u n ∈ A ) := 382 | begin 383 | split, 384 | { intro h, 385 | split, 386 | { exact h.1 }, 387 | -- On the next line, we don't need to tell Lean to treat `n+1` as a real number because 388 | -- we add `x` to it, so Lean knows there is only one way to make sense of this expression. 389 | have key : ∀ n : ℕ, ∃ a ∈ A, a < x + 1/(n+1), 390 | { intro n, 391 | -- we can use the lemma we proved above 392 | apply inf_lt h, 393 | -- and another one we proved! 394 | have : 0 < 1/(n+1 : ℝ), from inv_succ_pos n, 395 | linarith }, 396 | -- Now we need to use axiom of (countable) choice 397 | choose u hu using key, 398 | use u, 399 | split, 400 | { intros ε ε_pos, 401 | -- again we use a lemma we proved, specializing it to our fixed `ε`, and fixing a `N` 402 | cases limit_inv_succ ε ε_pos with N H, 403 | use N, 404 | intros n hn, 405 | have : x ≤ u n, from h.1 _ (hu n).1, 406 | have := calc 407 | u n < x + 1/(n + 1) : (hu n).2 408 | ... ≤ x + ε : add_le_add (le_refl x) (H n hn), 409 | rw abs_of_nonneg ; linarith }, 410 | { intro n, 411 | exact (hu n).1 } }, 412 | { intro h, 413 | -- Assumption `h` is made of nested compound statements. We can use the 414 | -- recursive version of `cases` to unpack it in one go. 415 | rcases h with ⟨x_min, u, lim, huA⟩, 416 | split, 417 | exact x_min, 418 | intros y y_mino, 419 | apply le_lim lim, 420 | intro n, 421 | exact y_mino (u n) (huA n) }, 422 | end 423 | 424 | 425 | -------------------------------------------------------------------------------- /src/exercises/01_equality_rewriting.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | 3 | /- 4 | One of the earliest kind of proofs one encounters while learning mathematics is proving by 5 | a calculation. It may not sound like a proof, but this is actually using lemmas expressing 6 | properties of operations on numbers. It also uses the fundamental property of equality: if two 7 | mathematical objects A and B are equal then, in any statement involving A, one can replace A 8 | by B. This operation is called rewriting, and the Lean "tactic" for this is `rw`. 9 | 10 | In the following exercises, we will use the following two lemmas: 11 | mul_assoc a b c : a * b * c = a * (b * c) 12 | mul_comm a b : a*b = b*a 13 | 14 | Hence the command 15 | rw mul_assoc a b c, 16 | will replace a*b*c by a*(b*c) in the current goal. 17 | 18 | In order to replace backward, we use 19 | rw ← mul_assoc a b c, 20 | replacing a*(b*c) by a*b*c in the current goal. 21 | 22 | Of course we don't want to constantly invoke those lemmas, and we will eventually introduce 23 | more powerful solutions. 24 | -/ 25 | 26 | -- Uncomment the following line if you want to see parentheses around subexpressions. 27 | -- set_option pp.parens true 28 | 29 | example (a b c : ℝ) : (a * b) * c = b * (a * c) := 30 | begin 31 | rw mul_comm a b, 32 | rw mul_assoc b a c, 33 | end 34 | 35 | -- 0001 36 | example (a b c : ℝ) : (c * b) * a = b * (a * c) := 37 | begin 38 | sorry 39 | end 40 | 41 | -- 0002 42 | example (a b c : ℝ) : a * (b * c) = b * (a * c) := 43 | begin 44 | sorry 45 | end 46 | 47 | /- 48 | Now let's return to the preceding example to experiment with what happens 49 | if we don't give arguments to mul_assoc or mul_comm. 50 | For instance, you can start the next proof with 51 | rw ← mul_assoc, 52 | Try to figure out what happens. 53 | -/ 54 | 55 | -- 0003 56 | example (a b c : ℝ) : a * (b * c) = b * (a * c) := 57 | begin 58 | sorry 59 | end 60 | 61 | /- 62 | We can also perform rewriting in an assumption of the local context, using for instance 63 | rw mul_comm a b at hyp, 64 | in order to replace a*b by b*a in assumption hyp. 65 | 66 | The next example will use a third lemma: 67 | two_mul a : 2*a = a + a 68 | 69 | Also we use the `exact` tactic, which allows to provide a direct proof term. 70 | -/ 71 | 72 | example (a b c d : ℝ) (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d := 73 | begin 74 | rw hyp' at hyp, 75 | rw mul_comm d a at hyp, 76 | rw ← two_mul (a*d) at hyp, 77 | rw ← mul_assoc 2 a d at hyp, 78 | exact hyp, -- Our assumption hyp is now exactly what we have to prove 79 | end 80 | 81 | /- 82 | And the next one can use: 83 | sub_self x : x - x = 0 84 | -/ 85 | 86 | -- 0004 87 | example (a b c d : ℝ) (hyp : c = b*a - d) (hyp' : d = a*b) : c = 0 := 88 | begin 89 | sorry 90 | end 91 | 92 | /- 93 | What is written in the two preceding example is very far away from what we would write on 94 | paper. Let's now see how to get a more natural layout. 95 | Inside each pair of curly braces below, the goal is to prove equality with the preceding line. 96 | -/ 97 | 98 | example (a b c d : ℝ) (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d := 99 | begin 100 | calc c = d*a + b : by { rw hyp } 101 | ... = d*a + a*d : by { rw hyp' } 102 | ... = a*d + a*d : by { rw mul_comm d a } 103 | ... = 2*(a*d) : by { rw two_mul } 104 | ... = 2*a*d : by { rw mul_assoc }, 105 | end 106 | 107 | /- 108 | Let's note there is no comma at the end of each line of calculation. `calc` is really one 109 | command, and the comma comes only after it's fully done. 110 | 111 | From a practical point of view, when writing such a proof, it is convenient to: 112 | * pause the tactic state view update in VScode by clicking the Pause icon button 113 | in the top right corner of the Lean Goal buffer 114 | * write the full calculation, ending each line with ": by {}" 115 | * resume tactic state update by clicking the Play icon button and fill in proofs between 116 | curly braces. 117 | 118 | Let's return to the other example using this method. 119 | -/ 120 | 121 | -- 0005 122 | example (a b c d : ℝ) (hyp : c = b*a - d) (hyp' : d = a*b) : c = 0 := 123 | begin 124 | sorry 125 | end 126 | 127 | /- 128 | The preceding proofs have exhausted our supply of "mul_comm" patience. Now it's time 129 | to get the computer to work harder. The `ring` tactic will prove any goal that follows by 130 | applying only the axioms of commutative (semi-)rings, in particular commutativity and 131 | associativity of addition and multiplication, as well as distributivity. 132 | 133 | We also note that curly braces are not necessary when we write a single tactic proof, so 134 | let's get rid of them. 135 | -/ 136 | 137 | example (a b c d : ℝ) (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d := 138 | begin 139 | calc c = d*a + b : by rw hyp 140 | ... = d*a + a*d : by rw hyp' 141 | ... = 2*a*d : by ring, 142 | end 143 | 144 | /- 145 | Of course we can use `ring` outside of `calc`. Let's do the next one in one line. 146 | -/ 147 | 148 | -- 0006 149 | example (a b c : ℝ) : a * (b * c) = b * (a * c) := 150 | begin 151 | sorry 152 | end 153 | 154 | /- 155 | This is too much fun. Let's do it again. 156 | -/ 157 | 158 | -- 0007 159 | example (a b : ℝ) : (a + b) + a = 2*a + b := 160 | begin 161 | sorry 162 | end 163 | 164 | /- 165 | Maybe this is cheating. Let's try to do the next computation without ring. 166 | We could use: 167 | pow_two x : x^2 = x*x 168 | mul_sub a b c : a*(b-c) = a*b - a*c 169 | add_mul a b c : (a+b)*c = a*c + b*c 170 | add_sub a b c : a + (b - c) = (a + b) - c 171 | sub_sub a b c : a - b - c = a - (b + c) 172 | add_zero a : a + 0 = a 173 | -/ 174 | 175 | -- 0008 176 | example (a b : ℝ) : (a + b)*(a - b) = a^2 - b^2 := 177 | begin 178 | sorry 179 | end 180 | 181 | /- Let's stick to ring in the end. -/ 182 | 183 | -------------------------------------------------------------------------------- /src/exercises/02_iff_if_and.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | 3 | /- 4 | In the previous file, we saw how to rewrite using equalities. 5 | The analogue operation with mathematical statements is rewriting using 6 | equivalences. This is also done using the `rw` tactic. 7 | Lean uses ↔ to denote equivalence instead of ⇔. 8 | 9 | In the following exercises we will use the lemma: 10 | 11 | sub_nonneg {x y : ℝ} : 0 ≤ y - x ↔ x ≤ y 12 | 13 | The curly braces around x and y instead of parentheses mean Lean will always try to figure out what 14 | x and y are from context, unless we really insist on telling it (we'll see how to insist much later). 15 | Let's not worry about that for now. 16 | 17 | In order to announce an intermediate statement we use: 18 | 19 | have my_name : my statement, 20 | 21 | This triggers the apparition of a new goal: proving the statement. After this is done, 22 | the statement becomes available under the name `my_name`. 23 | We can focus on the current goal by typing tactics between curly braces. 24 | -/ 25 | 26 | example {a b c : ℝ} (hab : a ≤ b) : c + a ≤ c + b := 27 | begin 28 | rw ← sub_nonneg, 29 | have key : (c + b) - (c + a) = b - a, -- Here we introduce an intermediate statement named key 30 | { ring, }, -- and prove it between curly braces 31 | rw key, -- we can now use the key statement 32 | rw sub_nonneg, 33 | exact hab, 34 | end 35 | 36 | /- 37 | Of course the previous lemma is already in the core library, named `add_le_add_left`, so we can use it below. 38 | 39 | Let's prove a variation (without invoking commutativity of addition since this would spoil our fun). 40 | -/ 41 | 42 | -- 0009 43 | example {a b : ℝ} (hab : a ≤ b) (c : ℝ) : a + c ≤ b + c := 44 | begin 45 | sorry 46 | end 47 | 48 | 49 | /- 50 | Let's see how we could use this lemma. It is already in the core library, under the name `add_le_add_right`: 51 | 52 | add_le_add_right {a b : ℝ} (hab : a ≤ b) (c : ℝ) : a + c ≤ b + c 53 | 54 | This can be read as: "add_le_add_right is a function that will take as input real numbers a and b, an 55 | assumption `hab` claiming a ≤ b and a real number c, and will output a proof of a + c ≤ b + c". 56 | 57 | In addition, recall that curly braces around a b mean Lean will figure out those arguments unless we 58 | insist to help. This is because they can be deduced from the next argument `hab`. 59 | So it will be sufficient to feed `hab` and c to this function. 60 | -/ 61 | 62 | example {a b : ℝ} (ha : 0 ≤ a) : b ≤ a + b := 63 | begin 64 | calc b = 0 + b : by ring 65 | ... ≤ a + b : by exact add_le_add_right ha b, 66 | end 67 | 68 | /- 69 | In the second line of the above proof, we need to prove 0 + b ≤ a + b. 70 | The proof after the colon says: this is exactly lemma `add_le_add_right` applied to ha and b. 71 | Actually the `calc` block expects proof terms, and the `by` keyword is used to tell Lean we will use tactics 72 | to build such a proof term. But since the only tactic used in this block is `exact`, we can skip 73 | tactics entirely, and write: 74 | -/ 75 | 76 | example (a b : ℝ) (ha : 0 ≤ a) : b ≤ a + b := 77 | begin 78 | calc b = 0 + b : by ring 79 | ... ≤ a + b : add_le_add_right ha b, 80 | end 81 | 82 | /- Let's do a variant. -/ 83 | 84 | -- 0010 85 | example (a b : ℝ) (hb : 0 ≤ b) : a ≤ a + b := 86 | begin 87 | sorry 88 | end 89 | 90 | /- 91 | The two preceding examples are in the core library : 92 | 93 | le_add_of_nonneg_left {a b : ℝ} (ha : 0 ≤ a) : b ≤ a + b 94 | le_add_of_nonneg_right {a b : ℝ} (hb : 0 ≤ b) : a ≤ a + b 95 | 96 | Again, there won't be any need to memorize those names, we will 97 | soon see how to get rid of such goals automatically. 98 | But we can already try to understand how their names are built: 99 | "le_add" describe the conclusion "less or equal than some addition" 100 | It comes first because we are focussed on proving stuff, and 101 | auto-completion works by looking at the beginning of words. 102 | "of" introduces assumptions. "nonneg" is Lean's abbreviation for non-negative. 103 | "left" or "right" disambiguates between the two variations. 104 | 105 | Let's use those lemmas by hand for now. 106 | 107 | Note that you can have several inequalities steps in a `calc` block, 108 | transitivity of inequalities will be used automatically to assemble 109 | the pieces. 110 | -/ 111 | 112 | -- 0011 113 | example (a b : ℝ) (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a + b := 114 | begin 115 | sorry 116 | end 117 | 118 | /- And let's combine with our earlier lemmas. -/ 119 | 120 | -- 0012 121 | example (a b c d : ℝ) (hab : a ≤ b) (hcd : c ≤ d) : a + c ≤ b + d := 122 | begin 123 | sorry 124 | end 125 | 126 | /- 127 | In the above examples, we prepared proofs of assumptions of our lemmas beforehand, so 128 | that we could feed them to the lemmas. This is called forward reasoning. 129 | The `calc` proofs also belong to this category. 130 | 131 | We can also announce the use of a lemma, and provide proofs after the fact, using 132 | the `apply` tactic. This is called backward reasoning because we get the conclusion 133 | first, and provide proofs later. Using `rw` on the goal (rather than on an assumption 134 | from the local context) is also backward reasoning. 135 | 136 | Let's do that using the lemma 137 | 138 | mul_nonneg {x y : ℝ} (hx : 0 ≤ x) (hy : 0 ≤ y) : 0 ≤ x*y 139 | -/ 140 | 141 | example (a b c : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c := 142 | begin 143 | rw ← sub_nonneg, 144 | have key : b*c - a*c = (b - a)*c, 145 | { ring }, 146 | rw key, 147 | apply mul_nonneg, -- Here we don't provide proofs for the lemma's assumptions 148 | -- Now we need to provide the proofs. 149 | { rw sub_nonneg, 150 | exact hab }, 151 | { exact hc }, 152 | end 153 | 154 | /- 155 | Let's prove the same statement using only forward reasoning: announcing stuff, 156 | proving it by working with known facts, moving forward. 157 | -/ 158 | 159 | example (a b c : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c := 160 | begin 161 | have hab' : 0 ≤ b - a, 162 | { rw ← sub_nonneg at hab, 163 | exact hab, }, 164 | have h₁ : 0 ≤ (b - a)*c, 165 | { exact mul_nonneg hab' hc }, 166 | have h₂ : (b - a)*c = b*c - a*c, 167 | { ring, }, 168 | have h₃ : 0 ≤ b*c - a*c, 169 | { rw h₂ at h₁, 170 | exact h₁, }, 171 | rw sub_nonneg at h₃, 172 | exact h₃, 173 | end 174 | 175 | /- 176 | One reason why the backward reasoning proof is shorter is because Lean can 177 | infer of lot of things by comparing the goal and the lemma statement. Indeed 178 | in the `apply mul_nonneg` line, we didn't need to tell Lean that x = b - a 179 | and y = c in the lemma. It was infered by "unification" between the lemma 180 | statement and the goal. 181 | 182 | To be fair to the forward reasoning version, we should introduce a convenient 183 | variation on `rw`. The `rwa` tactic performs rewrite and then looks for an 184 | assumption matching the goal. We can use it to rewrite our latest proof as: 185 | -/ 186 | 187 | example (a b c : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c := 188 | begin 189 | have hab' : 0 ≤ b - a, 190 | { rwa ← sub_nonneg at hab, }, 191 | have h₁ : 0 ≤ (b - a)*c, 192 | { exact mul_nonneg hab' hc }, 193 | have h₂ : (b - a)*c = b*c - a*c, 194 | { ring, }, 195 | have h₃ : 0 ≤ b*c - a*c, 196 | { rwa h₂ at h₁, }, 197 | rwa sub_nonneg at h₃, 198 | end 199 | 200 | /- 201 | Let's now combine forward and backward reasoning, to get our most 202 | efficient proof of this statement. Note in particular how unification is used 203 | to know what to prove inside the parentheses in the `mul_nonneg` arguments. 204 | -/ 205 | example (a b c : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c := 206 | begin 207 | rw ← sub_nonneg, 208 | calc 0 ≤ (b - a)*c : mul_nonneg (by rwa sub_nonneg) hc 209 | ... = b*c - a*c : by ring, 210 | end 211 | 212 | /- 213 | Let's now practice all three styles using: 214 | 215 | mul_nonneg_of_nonpos_of_nonpos {a b : α} (ha : a ≤ 0) (hb : b ≤ 0) : 0 ≤ a * b 216 | 217 | sub_nonpos {a b : α} : a - b ≤ 0 ↔ a ≤ b 218 | -/ 219 | 220 | /- First using mostly backward reasoning -/ 221 | -- 0013 222 | example (a b c : ℝ) (hc : c ≤ 0) (hab : a ≤ b) : b*c ≤ a*c := 223 | begin 224 | sorry 225 | end 226 | 227 | /- Using forward reasoning -/ 228 | -- 0014 229 | example (a b c : ℝ) (hc : c ≤ 0) (hab : a ≤ b) : b*c ≤ a*c := 230 | begin 231 | sorry 232 | end 233 | 234 | /-- Using a combination of both, with a `calc` block -/ 235 | -- 0015 236 | example (a b c : ℝ) (hc : c ≤ 0) (hab : a ≤ b) : b*c ≤ a*c := 237 | begin 238 | sorry 239 | end 240 | 241 | /- 242 | Let's now move to proving implications. Lean denotes implications using 243 | a simple arrow →, the same it uses for functions (say denoting the type of functions 244 | from ℕ to ℕ by ℕ → ℕ). This is because it sees a proof of P ⇒ Q as a function turning 245 | a proof of P into a proof Q. 246 | 247 | Many of the examples that we already met are implications under the hood. For instance we proved 248 | 249 | le_add_of_nonneg_left (a b : ℝ) (ha : 0 ≤ a) : b ≤ a + b 250 | 251 | But this can be rephrased as 252 | 253 | le_add_of_nonneg_left (a b : ℝ) : 0 ≤ a → b ≤ a + b 254 | 255 | In order to prove P → Q, we use the tactic `intros`, followed by an assumption name. 256 | This creates an assumption with that name asserting that P holds, and turns the goal into Q. 257 | 258 | Let's check we can go from our old version of `le_add_of_nonneg_left` to the new one. 259 | 260 | -/ 261 | example (a b : ℝ): 0 ≤ a → b ≤ a + b := 262 | begin 263 | intros ha, 264 | exact le_add_of_nonneg_left ha, 265 | end 266 | 267 | /- 268 | Actually Lean doesn't make any difference between those two versions. It is also happy with 269 | -/ 270 | example (a b : ℝ): 0 ≤ a → b ≤ a + b := 271 | le_add_of_nonneg_left 272 | 273 | /- No tactic state is shown in the above line because we don't even need to enter 274 | tactic mode using `begin` or `by`. 275 | 276 | Let's practise using `intros`. -/ 277 | 278 | -- 0016 279 | example (a b : ℝ): 0 ≤ b → a ≤ a + b := 280 | begin 281 | sorry 282 | end 283 | 284 | 285 | 286 | /- 287 | What about lemmas having more than one assumption? For instance: 288 | 289 | add_nonneg {a b : ℝ} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a + b 290 | 291 | A natural idea is to use the conjunction operator (logical AND), which Lean denotes 292 | by ∧. Assumptions built using this operator can be decomposed using the `cases` tactic, 293 | which is a very general assumption-decomposing tactic. 294 | -/ 295 | example {a b : ℝ} : (0 ≤ a ∧ 0 ≤ b) → 0 ≤ a + b := 296 | begin 297 | intros hyp, 298 | cases hyp with ha hb, 299 | exact add_nonneg ha hb, 300 | end 301 | 302 | /- 303 | Needing that intermediate line invoking `cases` shows this formulation is not what is used by 304 | Lean. It rather sees `add_nonneg` as two nested implications: 305 | if a is non-negative then if b is non-negative then a+b is non-negative. 306 | It reads funny, but it is much more convenient to use in practice. 307 | -/ 308 | example {a b : ℝ} : 0 ≤ a → (0 ≤ b → 0 ≤ a + b) := 309 | add_nonneg 310 | 311 | /- 312 | The above pattern is so common that implications are defined as right-associative operators, 313 | hence parentheses are not needed above. 314 | 315 | Let's prove that the naive conjunction version implies the funny Lean version. For this we need 316 | to know how to prove a conjunction. The `split` tactic creates two goals from a conjunction goal. 317 | It can also be used to create two implication goals from an equivalence goal. 318 | -/ 319 | example {a b : ℝ} (H : (0 ≤ a ∧ 0 ≤ b) → 0 ≤ a + b) : 0 ≤ a → (0 ≤ b → 0 ≤ a + b) := 320 | begin 321 | intros ha, 322 | intros hb, 323 | apply H, 324 | split, 325 | exact ha, 326 | exact hb, 327 | end 328 | 329 | /- 330 | Let's practice `cases` and `split`. In the next exercise, P, Q and R denote 331 | unspecified mathematical statements. 332 | -/ 333 | 334 | -- 0017 335 | example (P Q R : Prop) : P ∧ Q → Q ∧ P := 336 | begin 337 | sorry 338 | end 339 | 340 | /- 341 | Of course using `split` only to be able to use `exact` twice in a row feels silly. One can 342 | also use the anonymous constructor syntax: ⟨ ⟩ 343 | Beware those are not parentheses but angle brackets. You can type them as \< and \> in VS 344 | Code. They are a generic way of providing compound objects to Lean when Lean already has a 345 | very clear idea of what it is waiting for. 346 | 347 | So we could have replaced the last three lines by: 348 | exact ⟨hQ, hP⟩ 349 | 350 | We can also combine the `intros` steps. We can now compress our earlier proof to: 351 | -/ 352 | 353 | example {a b : ℝ} (H : (0 ≤ a ∧ 0 ≤ b) → 0 ≤ a + b) : 0 ≤ a → (0 ≤ b → 0 ≤ a + b) := 354 | begin 355 | intros ha hb, 356 | exact H ⟨ha, hb⟩, 357 | end 358 | 359 | /- 360 | The anonymous contructor trick actually also works in `intros` provided we use 361 | its recursive version `rintros`. So we can replace 362 | intro h, 363 | cases h with h₁ h₂ 364 | by 365 | rintros ⟨h₁, h₂⟩, 366 | Now redo the previous exercise using all those compressing techniques, in exactly two lines. -/ 367 | 368 | -- 0018 369 | example (P Q R : Prop): P ∧ Q → Q ∧ P := 370 | begin 371 | sorry 372 | end 373 | 374 | /- 375 | We are ready to come back to the equivalence between the different formulations of 376 | lemmas having two assumptions. Remember the `split` tactic can be used to split 377 | an equivalence into two implications. 378 | -/ 379 | 380 | -- 0019 381 | example (P Q R : Prop) : (P ∧ Q → R) ↔ (P → (Q → R)) := 382 | begin 383 | sorry 384 | end 385 | 386 | /- 387 | If you used more than five lines in the above exercise then try to compress things 388 | (without simply removing line ends). 389 | 390 | One last compression technique: given a proof h of a conjunction P ∧ Q, one can get 391 | a proof of P using h.left and a proof of Q using h.right, without using cases. 392 | One can also use the more generic (but less legible) names h.1 and h.2. 393 | 394 | Similarly, given a proof h of P ↔ Q, one can get a proof of P → Q using h.mp 395 | and a proof of Q → P using h.mpr (or the generic h.1 and h.2 that are even less legible 396 | in this case). 397 | 398 | Before the final exercise in this file, let's make sure we'll be able to leave 399 | without learning 10 lemma names. The `linarith` tactic will prove any equality or 400 | inequality or contradiction that follows by linear combinations of assumptions from the 401 | context (with constant coefficients). 402 | -/ 403 | 404 | example (a b : ℝ) (hb : 0 ≤ b) : a ≤ a + b := 405 | begin 406 | linarith, 407 | end 408 | 409 | /- 410 | Now let's enjoy this for a while. 411 | -/ 412 | 413 | -- 0020 414 | example (a b : ℝ) (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a + b := 415 | begin 416 | sorry 417 | end 418 | 419 | /- And let's combine with our earlier lemmas. -/ 420 | 421 | -- 0021 422 | example (a b c d : ℝ) (hab : a ≤ b) (hcd : c ≤ d) : a + c ≤ b + d := 423 | begin 424 | sorry 425 | end 426 | 427 | 428 | /- 429 | Final exercise 430 | 431 | In the last exercise of this file, we will use the divisibility relation on ℕ, 432 | denoted by ∣ (beware this is a unicode divisibility bar, not the ASCII pipe character), 433 | and the gcd function. 434 | 435 | The definitions are the usual ones, but our goal is to avoid using these definitions and 436 | only use the following three lemmas: 437 | 438 | dvd_refl (a : ℕ) : a ∣ a 439 | 440 | dvd_antisymm {a b : ℕ} : a ∣ b → b ∣ a → a = b := 441 | 442 | dvd_gcd_iff {a b c : ℕ} : c ∣ gcd a b ↔ c ∣ a ∧ c ∣ b 443 | -/ 444 | 445 | -- All functions and lemmas below are about natural numbers. 446 | open nat 447 | 448 | -- 0022 449 | example (a b : ℕ) : a ∣ b ↔ gcd a b = a := 450 | begin 451 | sorry 452 | end 453 | 454 | -------------------------------------------------------------------------------- /src/exercises/03_forall_or.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | import algebra.group.pi 3 | 4 | set_option pp.beta true 5 | 6 | /- 7 | In this file, we'll learn about the ∀ quantifier, and the disjunction 8 | operator ∨ (logical OR). 9 | 10 | Let P be a predicate on a type X. This means for every mathematical 11 | object x with type X, we get a mathematical statement P x. 12 | In Lean, P x has type Prop. 13 | 14 | Lean sees a proof h of `∀ x, P x` as a function sending any `x : X` to 15 | a proof `h x` of `P x`. 16 | This already explains the main way to use an assumption or lemma which 17 | starts with a ∀. 18 | 19 | In order to prove `∀ x, P x`, we use `intros x` to fix an arbitrary object 20 | with type X, and call it x. 21 | 22 | Note also we don't need to give the type of x in the expression `∀ x, P x` 23 | as long as the type of P is clear to Lean, which can then infer the type of x. 24 | 25 | Let's define two predicates to play with ∀. 26 | -/ 27 | 28 | def even_fun (f : ℝ → ℝ) := ∀ x, f (-x) = f x 29 | 30 | def odd_fun (f : ℝ → ℝ) := ∀ x, f (-x) = -f x 31 | 32 | /- 33 | In the next proof, we also take the opportunity to introduce the 34 | `unfold` tactic, which simply unfolds definitions. Here this is purely 35 | for didactic reason, Lean doesn't need those `unfold` invocations. 36 | We will also use `rfl` which is a term proving equalities that are true 37 | by definition (in a very strong sense to be discussed later). 38 | -/ 39 | 40 | example (f g : ℝ → ℝ) : even_fun f → even_fun g → even_fun (f + g) := 41 | begin 42 | -- Assume f is even 43 | intros hf, 44 | -- which means ∀ x, f (-x) = f x 45 | unfold even_fun at hf, 46 | -- and the same for g 47 | intros hg, 48 | unfold even_fun at hg, 49 | -- We need to prove ∀ x, (f+g)(-x) = (f+g)(x) 50 | unfold even_fun, 51 | -- Let x be any real number 52 | intros x, 53 | -- and let's compute 54 | calc (f + g) (-x) = f (-x) + g (-x) : rfl 55 | ... = f x + g (-x) : by rw hf x 56 | ... = f x + g x : by rw hg x 57 | ... = (f + g) x : rfl 58 | end 59 | 60 | /- 61 | In the preceding proof, all `unfold` lines are purely for 62 | psychological comfort. 63 | 64 | Sometimes unfolding is necessary because we want to apply a tactic 65 | that operates purely on the syntactical level. 66 | The main such tactic is `rw`. 67 | 68 | The same property of `rw` explain why the first computation line 69 | is necessary, although its proof is simply `rfl`. 70 | Before that line, `rw hf x` won't find anything like `f (-x)` hence 71 | will give up. 72 | The last line is not necessary however, since it only proves 73 | something that is true by definition, and is not followed by 74 | a `rw`. 75 | 76 | Also, Lean doesn't need to be told that hf should be specialized to 77 | x before rewriting, exactly as in the first file 01_equality_rewriting. 78 | We can also gather several rewrites using a list of expressions. 79 | 80 | One last trick is that `rw` can take a list of expressions to use for 81 | rewriting. For instance `rw [h₁, h₂, h₃]` is equivalent to three 82 | lines `rw h₁`, `rw h₂` and `rw h₃`. Note that you can inspect the tactic 83 | state between those rewrites when reading a proof using this syntax. You 84 | simply need to move the cursor inside the list. 85 | 86 | Hence we can compress the above proof to: 87 | -/ 88 | 89 | example (f g : ℝ → ℝ) : even_fun f → even_fun g → even_fun (f + g) := 90 | begin 91 | intros hf hg x, 92 | calc (f + g) (-x) = f (-x) + g (-x) : rfl 93 | ... = f x + g x : by rw [hf, hg] 94 | end 95 | 96 | /- 97 | Now let's practice. If you need to learn how to type a unicode symbol, 98 | you can put your mouse cursor above the symbol and wait for one second. 99 | -/ 100 | 101 | -- 0023 102 | example (f g : ℝ → ℝ) : even_fun f → even_fun (g ∘ f) := 103 | begin 104 | sorry 105 | end 106 | 107 | -- 0024 108 | example (f g : ℝ → ℝ) : odd_fun f → odd_fun g → odd_fun (g ∘ f) := 109 | begin 110 | sorry 111 | end 112 | 113 | /- 114 | Let's have more quantifiers, and play with forward and backward reasoning. 115 | 116 | In the next definitions, note how `∀ x₁, ∀ x₂` is abreviated to `∀ x₁ x₂`. 117 | -/ 118 | 119 | def non_decreasing (f : ℝ → ℝ) := ∀ x₁ x₂, x₁ ≤ x₂ → f x₁ ≤ f x₂ 120 | 121 | def non_increasing (f : ℝ → ℝ) := ∀ x₁ x₂, x₁ ≤ x₂ → f x₁ ≥ f x₂ 122 | 123 | /- Let's be very explicit and use forward reasoning first. -/ 124 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) := 125 | begin 126 | -- Let x₁ and x₂ be real numbers such that x₁ ≤ x₂ 127 | intros x₁ x₂ h, 128 | -- Since f is non-decreasing, f x₁ ≤ f x₂. 129 | have step₁ : f x₁ ≤ f x₂, 130 | exact hf x₁ x₂ h, 131 | -- Since g is non-decreasing, we then get g (f x₁) ≤ g (f x₂). 132 | exact hg (f x₁) (f x₂) step₁, 133 | end 134 | 135 | /- 136 | In the above proof, note how inconvenient it is to specify x₁ and x₂ in `hf x₁ x₂ h` since 137 | they could be inferred from the type of h. 138 | We could have written `hf _ _ h` and Lean would have filled the holes denoted by _. 139 | 140 | Even better we could have written the definition 141 | of `non_decreasing` as: ∀ {x₁ x₂}, x₁ ≤ x₂ → f x₁ ≤ f x₂, with curly braces to denote 142 | implicit arguments. 143 | 144 | But let's leave that aside for now. One possible variation on the above proof is to 145 | use the `specialize` tactic to replace hf by its specialization to the relevant value. 146 | -/ 147 | 148 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) := 149 | begin 150 | intros x₁ x₂ h, 151 | specialize hf x₁ x₂ h, 152 | exact hg (f x₁) (f x₂) hf, 153 | end 154 | 155 | /- 156 | This `specialize` tactic is mostly useful for exploration, or in preparation for rewriting 157 | in the assumption. One can very often replace its use by using more complicated expressions 158 | directly involving the original assumption, as in the next variation: 159 | -/ 160 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) := 161 | begin 162 | intros x₁ x₂ h, 163 | exact hg (f x₁) (f x₂) (hf x₁ x₂ h), 164 | end 165 | 166 | /- 167 | Since the above proof uses only `intros` and `exact`, we could very easily replace it by the 168 | raw proof term: 169 | -/ 170 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) := 171 | λ x₁ x₂ h, hg (f x₁) (f x₂) (hf x₁ x₂ h) 172 | 173 | /- 174 | Of course the above proof is difficult to decipher. The principle in mathlib is to use 175 | such a proof when the result is obvious and you don't want to read the proof anyway. 176 | 177 | Instead of pursuing this style, let's see how backward reasoning would look like here. 178 | As usual with this style, we use `apply` and enjoy Lean specializing assumptions for us 179 | using unification. 180 | -/ 181 | 182 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) := 183 | begin 184 | -- Let x₁ and x₂ be real numbers such that x₁ ≤ x₂ 185 | intros x₁ x₂ h, 186 | -- We need to prove (g ∘ f) x₁ ≤ (g ∘ f) x₂. 187 | -- Since g is non-decreasing, it suffices to prove f x₁ ≤ f x₂ 188 | apply hg, 189 | -- which follows from our assumption on f 190 | apply hf, 191 | -- and on x₁ and x₂ 192 | exact h 193 | end 194 | 195 | -- 0025 196 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_increasing g) : non_increasing (g ∘ f) := 197 | begin 198 | sorry 199 | end 200 | 201 | /- 202 | Let's switch to disjunctions now. Lean denotes by ∨ the 203 | logical OR operator. 204 | 205 | In order to make use of an assumption 206 | hyp : P ∨ Q 207 | we use the cases tactic: 208 | cases hyp with hP hQ 209 | which creates two proof branches: one branch assuming hP : P, 210 | and one branch assuming hQ : Q. 211 | 212 | In order to directly prove a goal P ∨ Q, 213 | we use either the `left` tactic and prove P or the `right` 214 | tactic and prove Q. 215 | 216 | In the next proof we use `ring` and `linarith` to get rid of 217 | easy computations or inequalities, as well as one lemma: 218 | 219 | mul_eq_zero : a*b = 0 ↔ a = 0 ∨ b = 0 220 | -/ 221 | 222 | example (a b : ℝ) : a = a*b → a = 0 ∨ b = 1 := 223 | begin 224 | intro hyp, 225 | have H : a*(1 - b) = 0, 226 | { calc a*(1 - b) = a - a*b : by ring 227 | ... = 0 : by linarith, }, 228 | rw mul_eq_zero at H, 229 | cases H with Ha Hb, 230 | { left, 231 | exact Ha, }, 232 | { right, 233 | linarith, }, 234 | end 235 | 236 | -- 0026 237 | example (x y : ℝ) : x^2 = y^2 → x = y ∨ x = -y := 238 | begin 239 | sorry 240 | end 241 | 242 | /- 243 | In the next exercise, we can use: 244 | eq_or_lt_of_le : x ≤ y → x = y ∨ x < y 245 | -/ 246 | 247 | -- 0027 248 | example (f : ℝ → ℝ) : non_decreasing f ↔ ∀ x y, x < y → f x ≤ f y := 249 | begin 250 | sorry 251 | end 252 | 253 | /- 254 | In the next exercise, we can use: 255 | le_total x y : x ≤ y ∨ y ≤ x 256 | -/ 257 | 258 | -- 0028 259 | example (f : ℝ → ℝ) (h : non_decreasing f) (h' : ∀ x, f (f x) = x) : ∀ x, f x = x := 260 | begin 261 | sorry 262 | end 263 | 264 | -------------------------------------------------------------------------------- /src/exercises/04_exists.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | import data.int.parity 3 | 4 | /- 5 | In this file, we learn how to handle the ∃ quantifier. 6 | 7 | In order to prove `∃ x, P x`, we give some x₀ using tactic `use x₀` and 8 | then prove `P x₀`. This x₀ can be an object from the local context 9 | or a more complicated expression. 10 | -/ 11 | example : ∃ n : ℕ, 8 = 2*n := 12 | begin 13 | use 4, 14 | refl, -- this is the tactic analogue of the rfl proof term 15 | end 16 | 17 | /- 18 | In order to use `h : ∃ x, P x`, we use the `cases` tactic to fix 19 | one x₀ that works. 20 | 21 | Again h can come straight from the local context or can be a more 22 | complicated expression. 23 | -/ 24 | example (n : ℕ) (h : ∃ k : ℕ, n = k + 1) : n > 0 := 25 | begin 26 | -- Let's fix k₀ such that n = k₀ + 1. 27 | cases h with k₀ hk₀, 28 | -- It now suffices to prove k₀ + 1 > 0. 29 | rw hk₀, 30 | -- and we have a lemma about this 31 | exact nat.succ_pos k₀, 32 | end 33 | 34 | /- 35 | The next exercises use divisibility in ℤ (beware the ∣ symbol which is 36 | not ASCII). 37 | 38 | By definition, a ∣ b ↔ ∃ k, b = a*k, so you can prove a ∣ b using the 39 | `use` tactic. 40 | -/ 41 | 42 | -- Until the end of this file, a, b and c will denote integers, unless 43 | -- explicitly stated otherwise 44 | variables (a b c : ℤ) 45 | 46 | -- 0029 47 | example (h₁ : a ∣ b) (h₂ : b ∣ c) : a ∣ c := 48 | begin 49 | sorry 50 | end 51 | 52 | /- 53 | A very common pattern is to have an assumption or lemma asserting 54 | h : ∃ x, y = ... 55 | and this is used through the combo: 56 | cases h with x hx, 57 | rw hx at ... 58 | The tactic `rcases` allows us to do recursive `cases`, as indicated by its name, 59 | and also simplifies the above combo when the name hx is replaced by the special 60 | name `rfl`, as in the following example. 61 | It uses the anonymous constructor angle brackets syntax. 62 | -/ 63 | 64 | 65 | example (h1 : a ∣ b) (h2 : a ∣ c) : a ∣ b+c := 66 | begin 67 | rcases h1 with ⟨k, rfl⟩, 68 | rcases h2 with ⟨l, rfl⟩, 69 | use k+l, 70 | ring, 71 | end 72 | 73 | /- 74 | You can use the same `rfl` trick with the `rintros` tactic. 75 | -/ 76 | 77 | example : a ∣ b → a ∣ c → a ∣ b+c := 78 | begin 79 | rintros ⟨k, rfl⟩ ⟨l, rfl⟩, 80 | use k+l, 81 | ring, 82 | end 83 | 84 | -- 0030 85 | example : 0 ∣ a ↔ a = 0 := 86 | begin 87 | sorry 88 | end 89 | 90 | /- 91 | We can now start combining quantifiers, using the definition 92 | 93 | surjective (f : X → Y) := ∀ y, ∃ x, f x = y 94 | 95 | -/ 96 | open function 97 | 98 | -- In the remaining of this file, f and g will denote functions from 99 | -- ℝ to ℝ. 100 | variables (f g : ℝ → ℝ) 101 | 102 | 103 | -- 0031 104 | example (h : surjective (g ∘ f)) : surjective g := 105 | begin 106 | sorry 107 | end 108 | 109 | /- 110 | The above exercise can be done in three lines. Try to do the 111 | next exercise in four lines. 112 | -/ 113 | 114 | -- 0032 115 | example (hf : surjective f) (hg : surjective g) : surjective (g ∘ f) := 116 | begin 117 | sorry 118 | end 119 | 120 | -------------------------------------------------------------------------------- /src/exercises/05_sequence_limits.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | import algebra.group.pi 3 | import tuto_lib 4 | 5 | notation `|`x`|` := abs x 6 | 7 | /- 8 | In this file we manipulate the elementary definition of limits of 9 | sequences of real numbers. 10 | mathlib has a much more general definition of limits, but here 11 | we want to practice using the logical operators and relations 12 | covered in the previous files. 13 | 14 | A sequence u is a function from ℕ to ℝ, hence Lean says 15 | u : ℕ → ℝ 16 | The definition we'll be using is: 17 | 18 | -- Definition of « u tends to l » 19 | def seq_limit (u : ℕ → ℝ) (l : ℝ) : Prop := 20 | ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε 21 | 22 | Note the use of `∀ ε > 0, ...` which is an abbreviation of 23 | `∀ ε, ε > 0 → ... ` 24 | 25 | In particular, a statement like `h : ∀ ε > 0, ...` 26 | can be specialized to a given ε₀ by 27 | `specialize h ε₀ hε₀` 28 | where hε₀ is a proof of ε₀ > 0. 29 | 30 | Also recall that, wherever Lean expects some proof term, we can 31 | start a tactic mode proof using the keyword `by` (followed by curly braces 32 | if you need more than one tactic invocation). 33 | For instance, if the local context contains: 34 | 35 | δ : ℝ 36 | δ_pos : δ > 0 37 | h : ∀ ε > 0, ... 38 | 39 | then we can specialize h to the real number δ/2 using: 40 | `specialize h (δ/2) (by linarith)` 41 | where `by linarith` will provide the proof of `δ/2 > 0` expected by Lean. 42 | 43 | We'll take this opportunity to use two new tactics: 44 | 45 | `norm_num` will perform numerical normalization on the goal and `norm_num at h` 46 | will do the same in assumption `h`. This will get rid of trivial calculations on numbers, 47 | like replacing |l - l| by zero in the next exercise. 48 | 49 | `congr'` will try to prove equalities between applications of functions by recursively 50 | proving the arguments are the same. 51 | For instance, if the goal is `f x + g y = f z + g t` then congr will replace it by 52 | two goals: `x = z` and `y = t`. 53 | You can limit the recursion depth by specifying a natural number after `congr'`. 54 | For instance, in the above example, `congr' 1` will give new goals 55 | `f x = f z` and `g y = g t`, which only inspect arguments of the addition and not deeper. 56 | -/ 57 | 58 | variables (u v w : ℕ → ℝ) (l l' : ℝ) 59 | 60 | -- If u is constant with value l then u tends to l 61 | -- 0033 62 | example : (∀ n, u n = l) → seq_limit u l := 63 | begin 64 | sorry 65 | end 66 | 67 | /- When dealing with absolute values, we'll use lemmas: 68 | 69 | abs_le {x y : ℝ} : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y 70 | 71 | abs_add (x y : ℝ) : |x + y| ≤ |x| + |y| 72 | 73 | abs_sub_comm (x y : ℝ) : |x - y| = |y - x| 74 | 75 | You should probably write them down on a sheet of paper that you keep at 76 | hand since they are used in many exercises. 77 | -/ 78 | 79 | -- Assume l > 0. Then u tends to l implies u n ≥ l/2 for large enough n 80 | -- 0034 81 | example (hl : l > 0) : seq_limit u l → ∃ N, ∀ n ≥ N, u n ≥ l/2 := 82 | begin 83 | sorry 84 | end 85 | 86 | /- 87 | When dealing with max, you can use 88 | 89 | ge_max_iff (p q r) : r ≥ max p q ↔ r ≥ p ∧ r ≥ q 90 | 91 | le_max_left p q : p ≤ max p q 92 | 93 | le_max_right p q : q ≤ max p q 94 | 95 | You should probably add them to the sheet of paper where you wrote 96 | the `abs` lemmas since they are used in many exercises. 97 | 98 | Let's see an example. 99 | -/ 100 | 101 | -- If u tends to l and v tends l' then u+v tends to l+l' 102 | example (hu : seq_limit u l) (hv : seq_limit v l') : 103 | seq_limit (u + v) (l + l') := 104 | begin 105 | intros ε ε_pos, 106 | cases hu (ε/2) (by linarith) with N₁ hN₁, 107 | cases hv (ε/2) (by linarith) with N₂ hN₂, 108 | use max N₁ N₂, 109 | intros n hn, 110 | cases ge_max_iff.mp hn with hn₁ hn₂, 111 | have fact₁ : |u n - l| ≤ ε/2, 112 | from hN₁ n (by linarith), -- note the use of `from`. 113 | -- This is an alias for `exact`, 114 | -- but reads nicer in this context 115 | have fact₂ : |v n - l'| ≤ ε/2, 116 | from hN₂ n (by linarith), 117 | calc 118 | |(u + v) n - (l + l')| = |u n + v n - (l + l')| : rfl 119 | ... = |(u n - l) + (v n - l')| : by congr' 1 ; ring 120 | ... ≤ |u n - l| + |v n - l'| : by apply abs_add 121 | ... ≤ ε : by linarith, 122 | end 123 | 124 | /- 125 | In the above proof, we used `have` to prepare facts for `linarith` consumption in the last line. 126 | Since we have direct proof terms for them, we can feed them directly to `linarith` as in the next proof 127 | of the same statement. 128 | Another variation we introduce is rewriting using `ge_max_iff` and letting `linarith` handle the 129 | conjunction, instead of creating two new assumptions. 130 | -/ 131 | 132 | example (hu : seq_limit u l) (hv : seq_limit v l') : 133 | seq_limit (u + v) (l + l') := 134 | begin 135 | intros ε ε_pos, 136 | cases hu (ε/2) (by linarith) with N₁ hN₁, 137 | cases hv (ε/2) (by linarith) with N₂ hN₂, 138 | use max N₁ N₂, 139 | intros n hn, 140 | rw ge_max_iff at hn, 141 | calc 142 | |(u + v) n - (l + l')| = |u n + v n - (l + l')| : rfl 143 | ... = |(u n - l) + (v n - l')| : by congr' 1 ; ring 144 | ... ≤ |u n - l| + |v n - l'| : by apply abs_add 145 | ... ≤ ε : by linarith [hN₁ n (by linarith), hN₂ n (by linarith)], 146 | end 147 | 148 | /- Let's do something similar: the squeezing theorem. -/ 149 | -- 0035 150 | example (hu : seq_limit u l) (hw : seq_limit w l) 151 | (h : ∀ n, u n ≤ v n) 152 | (h' : ∀ n, v n ≤ w n) : seq_limit v l := 153 | begin 154 | sorry 155 | 156 | end 157 | 158 | /- What about < ε? -/ 159 | -- 0036 160 | example (u l) : seq_limit u l ↔ 161 | ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| < ε := 162 | begin 163 | sorry 164 | end 165 | 166 | /- In the next exercise, we'll use 167 | 168 | eq_of_abs_sub_le_all (x y : ℝ) : (∀ ε > 0, |x - y| ≤ ε) → x = y 169 | -/ 170 | 171 | -- A sequence admits at most one limit 172 | -- 0037 173 | example : seq_limit u l → seq_limit u l' → l = l' := 174 | begin 175 | sorry 176 | end 177 | 178 | /- 179 | Let's now practice deciphering definitions before proving. 180 | -/ 181 | 182 | def non_decreasing (u : ℕ → ℝ) := ∀ n m, n ≤ m → u n ≤ u m 183 | 184 | def is_seq_sup (M : ℝ) (u : ℕ → ℝ) := 185 | (∀ n, u n ≤ M) ∧ ∀ ε > 0, ∃ n₀, u n₀ ≥ M - ε 186 | 187 | -- 0038 188 | example (M : ℝ) (h : is_seq_sup M u) (h' : non_decreasing u) : 189 | seq_limit u M := 190 | begin 191 | sorry 192 | end 193 | 194 | -------------------------------------------------------------------------------- /src/exercises/06_sub_sequences.lean: -------------------------------------------------------------------------------- 1 | import tuto_lib 2 | /- 3 | This file continues the elementary study of limits of sequences. 4 | It can be skipped if the previous file was too easy, it won't introduce 5 | any new tactic or trick. 6 | 7 | Remember useful lemmas: 8 | 9 | abs_le {x y : ℝ} : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y 10 | 11 | abs_add (x y : ℝ) : |x + y| ≤ |x| + |y| 12 | 13 | abs_sub_comm (x y : ℝ) : |x - y| = |y - x| 14 | 15 | ge_max_iff (p q r) : r ≥ max p q ↔ r ≥ p ∧ r ≥ q 16 | 17 | le_max_left p q : p ≤ max p q 18 | 19 | le_max_right p q : q ≤ max p q 20 | 21 | and the definition: 22 | 23 | def seq_limit (u : ℕ → ℝ) (l : ℝ) : Prop := 24 | ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε 25 | 26 | You can also use a property proved in the previous file: 27 | 28 | unique_limit : seq_limit u l → seq_limit u l' → l = l' 29 | 30 | def extraction (φ : ℕ → ℕ) := ∀ n m, n < m → φ n < φ m 31 | -/ 32 | 33 | 34 | variable { φ : ℕ → ℕ} 35 | 36 | /- 37 | The next lemma is proved by an easy induction, but we haven't seen induction 38 | in this tutorial. If you did the natural number game then you can delete 39 | the proof below and try to reconstruct it. 40 | -/ 41 | /-- An extraction is greater than id -/ 42 | lemma id_le_extraction' : extraction φ → ∀ n, n ≤ φ n := 43 | begin 44 | intros hyp n, 45 | induction n with n hn, 46 | { exact nat.zero_le _ }, 47 | { exact nat.succ_le_of_lt (by linarith [hyp n (n+1) (by linarith)]) }, 48 | end 49 | 50 | /-- Extractions take arbitrarily large values for arbitrarily large 51 | inputs. -/ 52 | -- 0039 53 | lemma extraction_ge : extraction φ → ∀ N N', ∃ n ≥ N', φ n ≥ N := 54 | begin 55 | sorry 56 | end 57 | 58 | /-- A real number `a` is a cluster point of a sequence `u` 59 | if `u` has a subsequence converging to `a`. 60 | 61 | def cluster_point (u : ℕ → ℝ) (a : ℝ) := 62 | ∃ φ, extraction φ ∧ seq_limit (u ∘ φ) a 63 | -/ 64 | 65 | variables {u : ℕ → ℝ} {a l : ℝ} 66 | 67 | /- 68 | In the exercise, we use `∃ n ≥ N, ...` which is the abbreviation of 69 | `∃ n, n ≥ N ∧ ...`. 70 | Lean can read this abbreviation, but displays it as the confusing: 71 | `∃ (n : ℕ) (H : n ≥ N)` 72 | One gets used to it. Alternatively, one can get rid of it using the lemma 73 | exists_prop {p q : Prop} : (∃ (h : p), q) ↔ p ∧ q 74 | -/ 75 | 76 | /-- If `a` is a cluster point of `u` then there are values of 77 | `u` arbitrarily close to `a` for arbitrarily large input. -/ 78 | -- 0040 79 | lemma near_cluster : 80 | cluster_point u a → ∀ ε > 0, ∀ N, ∃ n ≥ N, |u n - a| ≤ ε := 81 | begin 82 | sorry 83 | end 84 | 85 | /- 86 | The above exercice can be done in five lines. 87 | Hint: you can use the anonymous constructor syntax when proving 88 | existential statements. 89 | -/ 90 | 91 | /-- If `u` tends to `l` then its subsequences tend to `l`. -/ 92 | -- 0041 93 | lemma subseq_tendsto_of_tendsto' (h : seq_limit u l) (hφ : extraction φ) : 94 | seq_limit (u ∘ φ) l := 95 | begin 96 | sorry 97 | end 98 | 99 | /-- If `u` tends to `l` all its cluster points are equal to `l`. -/ 100 | -- 0042 101 | lemma cluster_limit (hl : seq_limit u l) (ha : cluster_point u a) : a = l := 102 | begin 103 | sorry 104 | end 105 | 106 | /-- Cauchy_sequence sequence -/ 107 | def cauchy_sequence (u : ℕ → ℝ) := ∀ ε > 0, ∃ N, ∀ p q, p ≥ N → q ≥ N → |u p - u q| ≤ ε 108 | 109 | -- 0043 110 | example : (∃ l, seq_limit u l) → cauchy_sequence u := 111 | begin 112 | sorry 113 | end 114 | 115 | 116 | /- 117 | In the next exercise, you can reuse 118 | near_cluster : cluster_point u a → ∀ ε > 0, ∀ N, ∃ n ≥ N, |u n - a| ≤ ε 119 | -/ 120 | -- 0044 121 | example (hu : cauchy_sequence u) (hl : cluster_point u l) : seq_limit u l := 122 | begin 123 | sorry 124 | end 125 | 126 | -------------------------------------------------------------------------------- /src/exercises/07_first_negations.lean: -------------------------------------------------------------------------------- 1 | import tuto_lib 2 | import data.int.parity 3 | /- 4 | Negations, proof by contradiction and contraposition. 5 | 6 | This file introduces the logical rules and tactics related to negation: 7 | exfalso, by_contradiction, contrapose, by_cases and push_neg. 8 | 9 | There is a special statement denoted by `false` which, by definition, 10 | has no proof. 11 | 12 | So `false` implies everything. Indeed `false → P` means any proof of 13 | `false` could be turned into a proof of P. 14 | This fact is known by its latin name 15 | "ex falso quod libet" (from false follows whatever you want). 16 | Hence Lean's tactic to invoke this is called `exfalso`. 17 | -/ 18 | 19 | example : false → 0 = 1 := 20 | begin 21 | intro h, 22 | exfalso, 23 | exact h, 24 | end 25 | 26 | /- 27 | The preceding example suggests that this definition of `false` isn't very useful. 28 | But actually it allows us to define the negation of a statement P as 29 | "P implies false" that we can read as "if P were true, we would get 30 | a contradiction". Lean denotes this by `¬ P`. 31 | 32 | One can prove that (¬ P) ↔ (P ↔ false). But in practice we directly 33 | use the definition of `¬ P`. 34 | -/ 35 | 36 | example {x : ℝ} : ¬ x < x := 37 | begin 38 | intro hyp, 39 | rw lt_iff_le_and_ne at hyp, 40 | cases hyp with hyp_inf hyp_non, 41 | clear hyp_inf, -- we won't use that one, so let's discard it 42 | change x = x → false at hyp_non, -- Lean doesn't need this psychological line 43 | apply hyp_non, 44 | refl, 45 | end 46 | 47 | open int 48 | 49 | -- 0045 50 | example (n : ℤ) (h_even : even n) (h_not_even : ¬ even n) : 0 = 1 := 51 | begin 52 | sorry 53 | end 54 | 55 | -- 0046 56 | example (P Q : Prop) (h₁ : P ∨ Q) (h₂ : ¬ (P ∧ Q)) : ¬ P ↔ Q := 57 | begin 58 | sorry 59 | end 60 | 61 | /- 62 | The definition of negation easily implies that, for every statement P, 63 | P → ¬ ¬ P 64 | 65 | The excluded middle axiom, which asserts P ∨ ¬ P allows us to 66 | prove the converse implication. 67 | 68 | Together those two implications form the principle of double negation elimination. 69 | not_not {P : Prop} : (¬ ¬ P) ↔ P 70 | 71 | The implication `¬ ¬ P → P` is the basis for proofs by contradiction: 72 | in order to prove P, it suffices to prove ¬¬ P, ie `¬ P → false`. 73 | 74 | Of course there is no need to keep explaining all this. The tactic 75 | `by_contradiction Hyp` will transform any goal P into `false` and 76 | add Hyp : ¬ P to the local context. 77 | 78 | Let's return to a proof from the 5th file: uniqueness of limits for a sequence. 79 | This cannot be proved without using some version of the excluded middle 80 | axiom. We used it secretely in 81 | 82 | eq_of_abs_sub_le_all (x y : ℝ) : (∀ ε > 0, |x - y| ≤ ε) → x = y 83 | 84 | (we'll prove a variation on this lemma below). 85 | 86 | In the proof below, we also take the opportunity to introduce the `let` tactic 87 | which creates a local definition. If needed, it can be unfolded using `dsimp` which 88 | takes a list of definitions to unfold. For instance after using `let N₀ := max N N'`, 89 | you could write `dsimp [N₀] at h` to replace `N₀` by its definition in some 90 | local assumption `h`. 91 | -/ 92 | example (u : ℕ → ℝ) (l l' : ℝ) : seq_limit u l → seq_limit u l' → l = l' := 93 | begin 94 | intros hl hl', 95 | by_contradiction H, 96 | change l ≠ l' at H, -- Lean does not need this line 97 | have ineg : |l-l'| > 0, 98 | exact abs_pos.mpr (sub_ne_zero_of_ne H), -- details about that line are not important 99 | cases hl ( |l-l'|/4 ) (by linarith) with N hN, 100 | cases hl' ( |l-l'|/4 ) (by linarith) with N' hN', 101 | let N₀ := max N N', 102 | specialize hN N₀ (le_max_left _ _), 103 | specialize hN' N₀ (le_max_right _ _), 104 | have clef : |l-l'| < |l-l'|, 105 | calc 106 | |l - l'| = |(l-u N₀) + (u N₀ -l')| : by ring_nf 107 | ... ≤ |l - u N₀| + |u N₀ - l'| : by apply abs_add 108 | ... = |u N₀ - l| + |u N₀ - l'| : by rw abs_sub_comm 109 | ... < |l-l'| : by linarith, 110 | linarith, -- linarith can also find simple numerical contradictions 111 | end 112 | 113 | /- 114 | Another incarnation of the excluded middle axiom is the principle of 115 | contraposition: in order to prove P ⇒ Q, it suffices to prove 116 | non Q ⇒ non P. 117 | -/ 118 | 119 | -- Using a proof by contradiction, let's prove the contraposition principle 120 | -- 0047 121 | example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q := 122 | begin 123 | sorry 124 | end 125 | 126 | /- 127 | Again Lean doesn't need this principle explained to it. We can use the 128 | `contrapose` tactic. 129 | -/ 130 | 131 | example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q := 132 | begin 133 | contrapose, 134 | exact h, 135 | end 136 | 137 | /- 138 | In the next exercise, we'll use 139 | odd n : ∃ k, n = 2*k + 1 140 | int.odd_iff_not_even {n : ℤ} : odd n ↔ ¬ even n 141 | -/ 142 | -- 0048 143 | example (n : ℤ) : even (n^2) ↔ even n := 144 | begin 145 | sorry 146 | end 147 | /- 148 | As a last step on our law of the excluded middle tour, let's notice that, especially 149 | in pure logic exercises, it can sometimes be useful to use the 150 | excluded middle axiom in its original form: 151 | classical.em : ∀ P, P ∨ ¬ P 152 | 153 | Instead of applying this lemma and then using the `cases` tactic, we 154 | have the shortcut 155 | by_cases h : P, 156 | 157 | combining both steps to create two proof branches: one assuming 158 | h : P, and the other assuming h : ¬ P 159 | 160 | For instance, let's prove a reformulation of this implication relation, 161 | which is sometimes used as a definition in other logical foundations, 162 | especially those based on truth tables (hence very strongly using 163 | excluded middle from the very beginning). 164 | -/ 165 | 166 | variables (P Q : Prop) 167 | 168 | example : (P → Q) ↔ (¬ P ∨ Q) := 169 | begin 170 | split, 171 | { intro h, 172 | by_cases hP : P, 173 | { right, 174 | exact h hP }, 175 | { left, 176 | exact hP } }, 177 | { intros h hP, 178 | cases h with hnP hQ, 179 | { exfalso, 180 | exact hnP hP }, 181 | { exact hQ } }, 182 | end 183 | 184 | -- 0049 185 | example : ¬ (P ∧ Q) ↔ ¬ P ∨ ¬ Q := 186 | begin 187 | sorry 188 | end 189 | 190 | /- 191 | It is crucial to understand negation of quantifiers. 192 | Let's do it by hand for a little while. 193 | In the first exercise, only the definition of negation is needed. 194 | -/ 195 | 196 | -- 0050 197 | example (n : ℤ) : ¬ (∃ k, n = 2*k) ↔ ∀ k, n ≠ 2*k := 198 | begin 199 | sorry 200 | end 201 | 202 | /- 203 | Contrary to negation of the existential quantifier, negation of the 204 | universal quantifier requires excluded middle for the first implication. 205 | In order to prove this, we can use either 206 | * a double proof by contradiction 207 | * a contraposition, not_not : (¬ ¬ P) ↔ P) and a proof by contradiction. 208 | -/ 209 | 210 | def even_fun (f : ℝ → ℝ) := ∀ x, f (-x) = f x 211 | 212 | -- 0051 213 | example (f : ℝ → ℝ) : ¬ even_fun f ↔ ∃ x, f (-x) ≠ f x := 214 | begin 215 | sorry 216 | end 217 | 218 | /- 219 | Of course we can't keep repeating the above proofs, especially the second one. 220 | So we use the `push_neg` tactic. 221 | -/ 222 | 223 | example : ¬ even_fun (λ x, 2*x) := 224 | begin 225 | unfold even_fun, -- Here unfolding is important because push_neg won't do it. 226 | push_neg, 227 | use 42, 228 | linarith, 229 | end 230 | 231 | -- 0052 232 | example (f : ℝ → ℝ) : ¬ even_fun f ↔ ∃ x, f (-x) ≠ f x := 233 | begin 234 | sorry 235 | end 236 | 237 | def bounded_above (f : ℝ → ℝ) := ∃ M, ∀ x, f x ≤ M 238 | 239 | example : ¬ bounded_above (λ x, x) := 240 | begin 241 | unfold bounded_above, 242 | push_neg, 243 | intro M, 244 | use M + 1, 245 | linarith, 246 | end 247 | 248 | -- Let's contrapose 249 | -- 0053 250 | example (x : ℝ) : (∀ ε > 0, x ≤ ε) → x ≤ 0 := 251 | begin 252 | sorry 253 | end 254 | 255 | /- 256 | The "contrapose, push_neg" combo is so common that we can abreviate it to 257 | `contrapose!` 258 | 259 | Let's use this trick, together with: 260 | eq_or_lt_of_le : a ≤ b → a = b ∨ a < b 261 | -/ 262 | 263 | -- 0054 264 | example (f : ℝ → ℝ) : (∀ x y, x < y → f x < f y) ↔ (∀ x y, (x ≤ y ↔ f x ≤ f y)) := 265 | begin 266 | sorry 267 | end 268 | 269 | -------------------------------------------------------------------------------- /src/exercises/07bis_abstract_negations.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | 3 | open_locale classical 4 | 5 | /- 6 | Theoretical negations. 7 | 8 | This file is for people interested in logic who want to fully understand 9 | negations. 10 | 11 | Here we don't use `contrapose` or `push_neg`. The goal is to prove lemmas 12 | that are used by those tactics. Of course we can use 13 | `exfalso`, `by_contradiction` and `by_cases`. 14 | 15 | If this doesn't sound like fun then skip ahead to the next file. 16 | -/ 17 | 18 | section negation_prop 19 | 20 | variables P Q : Prop 21 | 22 | -- 0055 23 | example : (P → Q) ↔ (¬ Q → ¬ P) := 24 | begin 25 | sorry 26 | end 27 | 28 | -- 0056 29 | lemma non_imp (P Q : Prop) : ¬ (P → Q) ↔ P ∧ ¬ Q := 30 | begin 31 | sorry 32 | end 33 | 34 | -- In the next one, let's use the axiom 35 | -- propext {P Q : Prop} : (P ↔ Q) → P = Q 36 | 37 | -- 0057 38 | example (P : Prop) : ¬ P ↔ P = false := 39 | begin 40 | sorry 41 | end 42 | 43 | end negation_prop 44 | 45 | section negation_quantifiers 46 | variables (X : Type) (P : X → Prop) 47 | 48 | -- 0058 49 | example : ¬ (∀ x, P x) ↔ ∃ x, ¬ P x := 50 | begin 51 | sorry 52 | end 53 | 54 | -- 0059 55 | example : ¬ (∃ x, P x) ↔ ∀ x, ¬ P x := 56 | begin 57 | sorry 58 | end 59 | 60 | -- 0060 61 | example (P : ℝ → Prop) : ¬ (∃ ε > 0, P ε) ↔ ∀ ε > 0, ¬ P ε := 62 | begin 63 | sorry 64 | end 65 | 66 | -- 0061 67 | example (P : ℝ → Prop) : ¬ (∀ x > 0, P x) ↔ ∃ x > 0, ¬ P x := 68 | begin 69 | sorry 70 | end 71 | 72 | end negation_quantifiers 73 | 74 | -------------------------------------------------------------------------------- /src/exercises/08_limits_negation.lean: -------------------------------------------------------------------------------- 1 | import tuto_lib 2 | 3 | 4 | section 5 | /- 6 | The first part of this file makes sure you can negate quantified statements 7 | in your head without the help of `push_neg`. 8 | 9 | You need to complete the statement and then use the `check_me` tactic 10 | to check your answer. This tactic exists only for those exercises, 11 | it mostly calls `push_neg` and then cleans up a bit. 12 | 13 | def seq_limit (u : ℕ → ℝ) (l : ℝ) : Prop := 14 | ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε 15 | -/ 16 | 17 | -- In this section, u denotes a sequence of real numbers 18 | -- f is a function from ℝ to ℝ 19 | -- x₀ and l are real numbers 20 | variables (u : ℕ → ℝ) (f : ℝ → ℝ) (x₀ l : ℝ) 21 | 22 | /- Negation of "u tends to l" -/ 23 | -- 0062 24 | example : ¬ (∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε) ↔ 25 | sorry 26 | := 27 | begin 28 | sorry 29 | end 30 | 31 | /- Negation of "f is continuous at x₀" -/ 32 | -- 0063 33 | example : ¬ (∀ ε > 0, ∃ δ > 0, ∀ x, |x - x₀| ≤ δ → |f x - f x₀| ≤ ε) ↔ 34 | sorry 35 | := 36 | begin 37 | sorry 38 | end 39 | 40 | /- 41 | In the next exercise, we need to keep in mind that 42 | `∀ x x', ...` is the abbreviation of 43 | `∀ x, ∀ x', ... `. 44 | 45 | Also, `∃ x x', ...` is the abbreviation of `∃ x, ∃ x', ...`. 46 | -/ 47 | 48 | /- Negation of "f is uniformly continuous on ℝ" -/ 49 | -- 0064 50 | example : ¬ (∀ ε > 0, ∃ δ > 0, ∀ x x', |x' - x| ≤ δ → |f x' - f x| ≤ ε) ↔ 51 | sorry 52 | := 53 | begin 54 | sorry 55 | end 56 | 57 | /- Negation of "f is sequentially continuous at x₀" -/ 58 | -- 0065 59 | example : ¬ (∀ u : ℕ → ℝ, (∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - x₀| ≤ ε) → (∀ ε > 0, ∃ N, ∀ n ≥ N, |(f ∘ u) n - f x₀| ≤ ε)) ↔ 60 | sorry 61 | := 62 | begin 63 | sorry 64 | end 65 | end 66 | 67 | /- 68 | We now turn to elementary applications of negations to limits of sequences. 69 | Remember that `linarith` can find easy numerical contradictions. 70 | 71 | Also recall the following lemmas: 72 | 73 | abs_le {x y : ℝ} : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y 74 | 75 | ge_max_iff (p q r) : r ≥ max p q ↔ r ≥ p ∧ r ≥ q 76 | 77 | le_max_left p q : p ≤ max p q 78 | 79 | le_max_right p q : q ≤ max p q 80 | 81 | /-- The sequence `u` tends to `+∞`. -/ 82 | def tendsto_infinity (u : ℕ → ℝ) := ∀ A, ∃ N, ∀ n ≥ N, u n ≥ A 83 | -/ 84 | 85 | -- 0066 86 | example {u : ℕ → ℝ} : tendsto_infinity u → ∀ l, ¬ seq_limit u l := 87 | begin 88 | sorry 89 | end 90 | 91 | def nondecreasing_seq (u : ℕ → ℝ) := ∀ n m, n ≤ m → u n ≤ u m 92 | 93 | -- 0067 94 | example (u : ℕ → ℝ) (l : ℝ) (h : seq_limit u l) (h' : nondecreasing_seq u) : 95 | ∀ n, u n ≤ l := 96 | begin 97 | sorry 98 | end 99 | 100 | /- 101 | In the following exercises, `A : set ℝ` means that A is a set of real numbers. 102 | We can use the usual notation x ∈ A. 103 | 104 | The notation `∀ x ∈ A, ...` is the abbreviation of `∀ x, x ∈ A → ... ` 105 | 106 | The notation `∃ x ∈ A, ...` is the abbreviation of `∃ x, x ∈ A ∧ ... `. 107 | More precisely it is the abbreviation of `∃ x (H : x ∈ A), ...` 108 | which is Lean's strange way of saying `∃ x, x ∈ A ∧ ... `. 109 | You can convert between these forms using the lemma 110 | exists_prop {p q : Prop} : (∃ (h : p), q) ↔ p ∧ q 111 | 112 | We'll work with upper bounds and supremums. 113 | Again we'll introduce specialized definitions for the sake of exercises, but mathlib 114 | has more general versions. 115 | 116 | 117 | def upper_bound (A : set ℝ) (x : ℝ) := ∀ a ∈ A, a ≤ x 118 | 119 | def is_sup (A : set ℝ) (x : ℝ) := upper_bound A x ∧ ∀ y, upper_bound A y → x ≤ y 120 | 121 | 122 | Remark: one can easily show that a set of real numbers has at most one sup, 123 | but we won't need this. 124 | -/ 125 | 126 | -- 0068 127 | example {A : set ℝ} {x : ℝ} (hx : is_sup A x) : 128 | ∀ y, y < x → ∃ a ∈ A, y < a := 129 | begin 130 | sorry 131 | end 132 | 133 | /- 134 | Let's do a variation on an example from file 07 that will be useful in the last 135 | exercise below. 136 | -/ 137 | 138 | -- 0069 139 | lemma le_of_le_add_all' {x y : ℝ} : 140 | (∀ ε > 0, y ≤ x + ε) → y ≤ x := 141 | begin 142 | sorry 143 | end 144 | 145 | -- 0070 146 | example {x y : ℝ} {u : ℕ → ℝ} (hu : seq_limit u x) 147 | (ineg : ∀ n, u n ≤ y) : x ≤ y := 148 | begin 149 | sorry 150 | end 151 | 152 | -------------------------------------------------------------------------------- /src/exercises/09_limits_final.lean: -------------------------------------------------------------------------------- 1 | import tuto_lib 2 | 3 | set_option pp.beta true 4 | set_option pp.coercions false 5 | 6 | /- 7 | This is the final file in the series. Here we use everything covered 8 | in previous files to prove a couple of famous theorems from 9 | elementary real analysis. Of course they all have more general versions 10 | in mathlib. 11 | 12 | As usual, keep in mind the following: 13 | 14 | abs_le {x y : ℝ} : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y 15 | 16 | ge_max_iff (p q r) : r ≥ max p q ↔ r ≥ p ∧ r ≥ q 17 | 18 | le_max_left p q : p ≤ max p q 19 | 20 | le_max_right p q : q ≤ max p q 21 | 22 | as well as a lemma from the previous file: 23 | 24 | le_of_le_add_all : (∀ ε > 0, y ≤ x + ε) → y ≤ x 25 | 26 | Let's start with a variation on a known exercise. 27 | -/ 28 | 29 | -- 0071 30 | lemma le_lim {x y : ℝ} {u : ℕ → ℝ} (hu : seq_limit u x) 31 | (ineg : ∃ N, ∀ n ≥ N, y ≤ u n) : y ≤ x := 32 | begin 33 | sorry 34 | end 35 | 36 | /- 37 | Let's now return to the result proved in the `00_` file of this series, 38 | and prove again the sequential characterization of upper bounds (with a slighly 39 | different proof). 40 | 41 | For this, and other exercises below, we'll need many things that we proved in previous files, 42 | and a couple of extras. 43 | 44 | From the 5th file: 45 | 46 | limit_const (x : ℝ) : seq_limit (λ n, x) x 47 | 48 | 49 | squeeze (lim_u : seq_limit u l) (lim_w : seq_limit w l) 50 | (hu : ∀ n, u n ≤ v n) (hw : ∀ n, v n ≤ w n) : seq_limit v l 51 | 52 | From the 8th: 53 | 54 | def upper_bound (A : set ℝ) (x : ℝ) := ∀ a ∈ A, a ≤ x 55 | 56 | def is_sup (A : set ℝ) (x : ℝ) := upper_bound A x ∧ ∀ y, upper_bound A y → x ≤ y 57 | 58 | lt_sup (hx : is_sup A x) : ∀ y, y < x → ∃ a ∈ A, y < a := 59 | 60 | You can also use: 61 | 62 | nat.one_div_pos_of_nat {n : ℕ} : 0 < 1 / (n + 1 : ℝ) 63 | 64 | inv_succ_le_all : ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, 1/(n + 1 : ℝ) ≤ ε 65 | 66 | and their easy consequences: 67 | 68 | limit_of_sub_le_inv_succ (h : ∀ n, |u n - x| ≤ 1/(n+1)) : seq_limit u x 69 | 70 | limit_const_add_inv_succ (x : ℝ) : seq_limit (λ n, x + 1/(n+1)) x 71 | 72 | limit_const_sub_inv_succ (x : ℝ) : seq_limit (λ n, x - 1/(n+1)) x 73 | 74 | as well as: 75 | 76 | lim_le (hu : seq_limit u x) (ineg : ∀ n, u n ≤ y) : x ≤ y 77 | 78 | The structure of the proof is offered. It features a new tactic: 79 | `choose` which invokes the axiom of choice (observing the tactic state before and 80 | after using it should be enough to understand everything). 81 | -/ 82 | 83 | -- 0072 84 | lemma is_sup_iff (A : set ℝ) (x : ℝ) : 85 | (is_sup A x) ↔ (upper_bound A x ∧ ∃ u : ℕ → ℝ, seq_limit u x ∧ ∀ n, u n ∈ A ) := 86 | begin 87 | split, 88 | { intro h, 89 | split, 90 | { 91 | sorry 92 | }, 93 | { have : ∀ n : ℕ, ∃ a ∈ A, x - 1/(n+1) < a, 94 | { intros n, 95 | have : 1/(n+1 : ℝ) > 0, 96 | exact nat.one_div_pos_of_nat, 97 | sorry 98 | }, 99 | choose u hu using this, 100 | sorry 101 | } }, 102 | { rintro ⟨maj, u, limu, u_in⟩, 103 | sorry 104 | }, 105 | end 106 | 107 | /-- Continuity of a function at a point -/ 108 | def continuous_at_pt (f : ℝ → ℝ) (x₀ : ℝ) : Prop := 109 | ∀ ε > 0, ∃ δ > 0, ∀ x, |x - x₀| ≤ δ → |f x - f x₀| ≤ ε 110 | 111 | variables {f : ℝ → ℝ} {x₀ : ℝ} {u : ℕ → ℝ} 112 | 113 | -- 0073 114 | lemma seq_continuous_of_continuous (hf : continuous_at_pt f x₀) 115 | (hu : seq_limit u x₀) : seq_limit (f ∘ u) (f x₀) := 116 | begin 117 | sorry 118 | end 119 | 120 | -- 0074 121 | example : 122 | (∀ u : ℕ → ℝ, seq_limit u x₀ → seq_limit (f ∘ u) (f x₀)) → 123 | continuous_at_pt f x₀ := 124 | begin 125 | sorry 126 | end 127 | 128 | /- 129 | Recall from the 6th file: 130 | 131 | 132 | def extraction (φ : ℕ → ℕ) := ∀ n m, n < m → φ n < φ m 133 | 134 | def cluster_point (u : ℕ → ℝ) (a : ℝ) := 135 | ∃ φ, extraction φ ∧ seq_limit (u ∘ φ) a 136 | 137 | 138 | id_le_extraction : extraction φ → ∀ n, n ≤ φ n 139 | 140 | and from the 8th file: 141 | 142 | def tendsto_infinity (u : ℕ → ℝ) := ∀ A, ∃ N, ∀ n ≥ N, u n ≥ A 143 | 144 | not_seq_limit_of_tendstoinfinity : tendsto_infinity u → ∀ l, ¬ seq_limit u l 145 | -/ 146 | 147 | variables {φ : ℕ → ℕ} 148 | 149 | 150 | -- 0075 151 | lemma subseq_tendstoinfinity 152 | (h : tendsto_infinity u) (hφ : extraction φ) : 153 | tendsto_infinity (u ∘ φ) := 154 | begin 155 | sorry 156 | end 157 | 158 | -- 0076 159 | lemma squeeze_infinity {u v : ℕ → ℝ} (hu : tendsto_infinity u) 160 | (huv : ∀ n, u n ≤ v n) : tendsto_infinity v := 161 | begin 162 | sorry 163 | end 164 | 165 | /- 166 | We will use segments: Icc a b := { x | a ≤ x ∧ x ≤ b } 167 | The notation stands for Interval-closed-closed. Variations exist with 168 | o or i instead of c, where o stands for open and i for infinity. 169 | 170 | We will use the following version of Bolzano-Weierstrass 171 | 172 | bolzano_weierstrass (h : ∀ n, u n ∈ [a, b]) : 173 | ∃ c ∈ [a, b], cluster_point u c 174 | 175 | as well as the obvious 176 | 177 | seq_limit_id : tendsto_infinity (λ n, n) 178 | -/ 179 | open set 180 | 181 | 182 | -- 0077 183 | lemma bdd_above_segment {f : ℝ → ℝ} {a b : ℝ} (hf : ∀ x ∈ Icc a b, continuous_at_pt f x) : 184 | ∃ M, ∀ x ∈ Icc a b, f x ≤ M := 185 | begin 186 | sorry 187 | end 188 | 189 | /- 190 | In the next exercise, we can use: 191 | 192 | abs_neg x : |-x| = |x| 193 | -/ 194 | 195 | -- 0078 196 | lemma continuous_opposite {f : ℝ → ℝ} {x₀ : ℝ} (h : continuous_at_pt f x₀) : 197 | continuous_at_pt (λ x, -f x) x₀ := 198 | begin 199 | sorry 200 | end 201 | 202 | /- 203 | Now let's combine the two exercises above 204 | -/ 205 | 206 | -- 0079 207 | lemma bdd_below_segment {f : ℝ → ℝ} {a b : ℝ} (hf : ∀ x ∈ Icc a b, continuous_at_pt f x) : 208 | ∃ m, ∀ x ∈ Icc a b, m ≤ f x := 209 | begin 210 | sorry 211 | end 212 | 213 | /- 214 | Remember from the 5th file: 215 | 216 | unique_limit : seq_limit u l → seq_limit u l' → l = l' 217 | 218 | and from the 6th one: 219 | 220 | subseq_tendsto_of_tendsto (h : seq_limit u l) (hφ : extraction φ) : 221 | seq_limit (u ∘ φ) l 222 | 223 | We now admit the following version of the least upper bound theorem 224 | (that cannot be proved without discussing the construction of real numbers 225 | or admitting another strong theorem). 226 | 227 | sup_segment {a b : ℝ} {A : set ℝ} (hnonvide : ∃ x, x ∈ A) (h : A ⊆ Icc a b) : 228 | ∃ x ∈ Icc a b, is_sup A x 229 | 230 | In the next exercise, it can be useful to prove inclusions of sets of real number. 231 | By definition, A ⊆ B means : ∀ x, x ∈ A → x ∈ B. 232 | Hence one can start a proof of A ⊆ B by `intros x x_in`, 233 | which brings `x : ℝ` and `x_in : x ∈ A` in the local context, 234 | and then prove `x ∈ B`. 235 | 236 | Note also the use of 237 | {x | P x} 238 | which denotes the set of x satisfying predicate P. 239 | 240 | Hence `x' ∈ { x | P x} ↔ P x'`, by definition. 241 | -/ 242 | 243 | -- 0080 244 | example {a b : ℝ} (hab : a ≤ b) (hf : ∀ x ∈ Icc a b, continuous_at_pt f x) : 245 | ∃ x₀ ∈ Icc a b, ∀ x ∈ Icc a b, f x ≤ f x₀ := 246 | begin 247 | sorry 248 | end 249 | 250 | lemma stupid {a b x : ℝ} (h : x ∈ Icc a b) (h' : x ≠ b) : x < b := 251 | lt_of_le_of_ne h.right h' 252 | 253 | /- 254 | And now the final boss... 255 | -/ 256 | 257 | def I := (Icc 0 1 : set ℝ) -- the type ascription makes sure 0 and 1 are real numbers here 258 | 259 | -- 0081 260 | example (f : ℝ → ℝ) (hf : ∀ x, continuous_at_pt f x) (h₀ : f 0 < 0) (h₁ : f 1 > 0) : 261 | ∃ x₀ ∈ I, f x₀ = 0 := 262 | begin 263 | let A := { x | x ∈ I ∧ f x < 0}, 264 | have ex_x₀ : ∃ x₀ ∈ I, is_sup A x₀, 265 | { 266 | sorry 267 | }, 268 | rcases ex_x₀ with ⟨x₀, x₀_in, x₀_sup⟩, 269 | use [x₀, x₀_in], 270 | have : f x₀ ≤ 0, 271 | { 272 | sorry 273 | }, 274 | have x₀_1: x₀ < 1, 275 | { 276 | sorry 277 | }, 278 | have : f x₀ ≥ 0, 279 | { have in_I : ∃ N : ℕ, ∀ n ≥ N, x₀ + 1/(n+1) ∈ I, 280 | { have : ∃ N : ℕ, ∀ n≥ N, 1/(n+1 : ℝ) ≤ 1-x₀, 281 | { 282 | sorry 283 | }, 284 | sorry 285 | }, 286 | have not_in : ∀ n : ℕ, x₀ + 1/(n+1) ∉ A, 287 | -- By definition, x ∉ A means ¬ (x ∈ A). 288 | { 289 | sorry 290 | }, 291 | dsimp [A] at not_in, 292 | sorry 293 | }, 294 | linarith, 295 | end 296 | 297 | -------------------------------------------------------------------------------- /src/solutions/00_first_proofs.lean: -------------------------------------------------------------------------------- 1 | /- 2 | This file is intended for Lean beginners. The goal is to demonstrate what it feels like to prove 3 | things using Lean and mathlib. Complicated definitions and theory building are not covered. 4 | Everything is covered again more slowly and with exercises in the next files. 5 | -/ 6 | 7 | -- We want real numbers and their basic properties 8 | import data.real.basic 9 | 10 | -- We want to be able to use Lean's built-in "help" functionality 11 | import tactic.suggest 12 | 13 | -- We want to be able to define functions using the law of excluded middle 14 | noncomputable theory 15 | open_locale classical 16 | 17 | 18 | /- 19 | Our first goal is to define the set of upper bounds of a set of real numbers. 20 | This is already defined in mathlib (in a more general context), but we repeat 21 | it for the sake of exposition. Right-click "upper_bounds" below to get offered 22 | to jump to mathlib's version 23 | -/ 24 | #check upper_bounds 25 | 26 | /-- The set of upper bounds of a set of real numbers ℝ -/ 27 | def up_bounds (A : set ℝ) := { x : ℝ | ∀ a ∈ A, a ≤ x} 28 | 29 | /-- Predicate `is_maximum a A` means `a` is a maximum of `A` -/ 30 | def is_maximum (a : ℝ) (A : set ℝ) := a ∈ A ∧ a ∈ up_bounds A 31 | 32 | /- 33 | In the above definition, the symbol `∧` means "and". We also see the most 34 | visible difference between set theoretic foundations and type theoretic ones 35 | (used by almost all proof assistants). In set theory, everything is a set, and the 36 | only relation you get from foundations are `=` and `∈`. In type theory, there is 37 | a meta-theoretic relation of "typing": `a : ℝ` reads "`a` is a real number" or, 38 | more precisely, "the type of `a` is `ℝ`". Here "meta-theoretic" means this is not a 39 | statement you can prove or disprove inside the theory, it's a fact that is true or 40 | not. Here we impose this fact, in other circumstances, it would be checked by the 41 | Lean kernel. 42 | By contrast, `a ∈ A` is a statement inside the theory. Here it's part of the 43 | definition, in other circumstances it could be something proven inside Lean. 44 | -/ 45 | 46 | /- For illustrative purposes, we now define an infix version of the above predicate. 47 | It will allow us to write `a is_a_max_of A`, which is closer to a sentence. 48 | -/ 49 | infix ` is_a_max_of `:55 := is_maximum 50 | 51 | /- 52 | Let's prove something now! A set of real numbers has at most one maximum. Here 53 | everything left of the final `:` is introducing the objects and assumption. The equality 54 | `x = y` right of the colon is the conclusion. 55 | -/ 56 | lemma unique_max (A : set ℝ) (x y : ℝ) (hx : x is_a_max_of A) (hy : y is_a_max_of A) : x = y := 57 | begin 58 | -- We first break our assumptions in their two constituent pieces. 59 | -- We are free to choose the name following `with` 60 | cases hx with x_in x_up, 61 | cases hy with y_in y_up, 62 | -- Assumption `x_up` means x isn't less than elements of A, let's apply this to y 63 | specialize x_up y, 64 | -- Assumption `x_up` now needs the information that `y` is indeed in `A`. 65 | specialize x_up y_in, 66 | -- Let's do this quicker with roles swapped 67 | specialize y_up x x_in, 68 | -- We explained to Lean the idea of this proof. 69 | -- Now we know `x ≤ y` and `y ≤ x`, and Lean shouldn't need more help. 70 | -- `linarith` proves equalities and inequalities that follow linearly from 71 | -- the assumption we have. 72 | linarith, 73 | end 74 | 75 | /- 76 | The above proof is too long, even if you remove comments. We don't really need the 77 | unpacking steps at the beginning; we can access both parts of the assumption 78 | `hx : x is_a_max_of A` using shortcuts `hx.1` and `hx.2`. We can also improve 79 | readability without assistance from the tactic state display, clearly announcing 80 | intermediate goals using `have`. This way we get to the following version of the 81 | same proof. 82 | -/ 83 | 84 | example (A : set ℝ) (x y : ℝ) (hx : x is_a_max_of A) (hy : y is_a_max_of A) : x = y := 85 | begin 86 | have : x ≤ y, from hy.2 x hx.1, 87 | have : y ≤ x, from hx.2 y hy.1, 88 | linarith, 89 | end 90 | 91 | /- 92 | Notice how mathematics based on type theory treats the assumption 93 | `∀ a ∈ A, a ≤ y` as a function turning an element `a` of `A` into the statement 94 | `a ≤ y`. More precisely, this assumption is the abbreviation of 95 | `∀ a : ℝ, a ∈ A → a ≤ y`. The expression `hy.2 x` appearing in the above proof 96 | is then the statement `x ∈ A → x ≤ y`, which itself is a function turning a 97 | statement `x ∈ A` into `x ≤ y` so that the full expression `hy.2 x hx.1` is 98 | indeed a proof of `x ≤ y`. 99 | 100 | One could argue a three-line-long proof of this lemma is still two lines too long. 101 | This is debatable, but mathlib's style is to write very short proofs for trivial 102 | lemmas. Those proofs are not easy to read but they are meant to indicate that the 103 | proof is probably not worth reading. 104 | 105 | In order to reach this stage, we need to know what `linarith` did for us. It invoked 106 | the lemma `le_antisymm` which says: `x ≤ y → y ≤ x → x = y`. This arrow, which 107 | is used both for function and implication, is right associative. So the statement is 108 | `x ≤ y → (y ≤ x → x = y)` which reads: I will send a proof `p` of `x ≤ y` to a function 109 | sending a proof `q'` of `y ≤ x` to a proof of `x = y`. Hence `le_antisymm p q'` is a 110 | proof of `x = y`. 111 | 112 | Using this we can get our one-line proof: 113 | -/ 114 | 115 | example (A : set ℝ) (x y : ℝ) (hx : x is_a_max_of A) (hy : y is_a_max_of A) : x = y := 116 | le_antisymm (hy.2 x hx.1) (hx.2 y hy.1) 117 | 118 | /- 119 | Such a proof is called a proof term (or a "term mode" proof). Notice it has no `begin` 120 | and `end`. It is directly the kind of low level proof that the Lean kernel is 121 | consuming. Commands like `cases`, `specialize` or `linarith` are called tactics, they 122 | help users constructing proof terms that could be very tedious to write directly. 123 | The most efficient proof style combines tactics with proof terms like our previous 124 | `have : x ≤ y, from hy.2 x hx.1` where `hy.2 x hx.1` is a proof term embeded inside 125 | a tactic mode proof. 126 | 127 | In the remaining of this file, we'll be characterizing infima of sets of real numbers 128 | in term of sequences. 129 | -/ 130 | 131 | /-- The set of lower bounds of a set of real numbers ℝ -/ 132 | def low_bounds (A : set ℝ) := { x : ℝ | ∀ a ∈ A, x ≤ a} 133 | 134 | /- 135 | We now define `a` is an infimum of `A`. Again there is already a more general version 136 | in mathlib. 137 | -/ 138 | def is_inf (x : ℝ) (A : set ℝ) := x is_a_max_of (low_bounds A) 139 | infix ` is_an_inf_of `:55 := is_inf 140 | 141 | /- 142 | We need to prove that any number which is greater than the infimum of A is greater 143 | than some element of A. 144 | -/ 145 | 146 | lemma inf_lt {A : set ℝ} {x : ℝ} (hx : x is_an_inf_of A) : 147 | ∀ y, x < y → ∃ a ∈ A, a < y := 148 | begin 149 | -- Let `y` be any real number. 150 | intro y, 151 | -- Let's prove the contrapositive 152 | contrapose, 153 | -- The symbol `¬` means negation. Let's ask Lean to rewrite the goal without negation, 154 | -- pushing negation through quantifiers and inequalities 155 | push_neg, 156 | -- Let's assume the premise, calling the assumption `h` 157 | intro h, 158 | -- `h` is exactly saying `y` is a lower bound of `A` so the second part of 159 | -- the infimum assumption `hx` applied to `y` and `h` is exactly what we want. 160 | exact hx.2 y h 161 | end 162 | 163 | /- 164 | In the above proof, the sequence `contrapose, push_neg` is so common that it can be 165 | abbreviated to `contrapose!`. With these commands, we enter the gray zone between 166 | proof checking and proof finding. Practical computer proof checking crucially needs 167 | the computer to handle tedious proof steps. In the next proof, we'll start using 168 | `linarith` a bit more seriously, going one step further into automation. 169 | 170 | Our next real goal is to prove inequalities for limits of sequences. We extract the 171 | following lemma: if `y ≤ x + ε` for all positive `ε` then `y ≤ x`. 172 | -/ 173 | 174 | 175 | lemma le_of_le_add_eps {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) → y ≤ x := 176 | begin 177 | -- Let's prove the contrapositive, asking Lean to push negations right away. 178 | contrapose!, 179 | -- Assume `h : x < y`. 180 | intro h, 181 | -- We need to find `ε` such that `ε` is positive and `x + ε < y`. 182 | -- Let's use `(y-x)/2` 183 | use ((y-x)/2), 184 | -- we now have two properties to prove. Let's do both in turn, using `linarith` 185 | split, 186 | linarith, 187 | linarith, 188 | end 189 | 190 | /- 191 | Note how `linarith` was used for both sub-goals at the end of the above proof. 192 | We could have shortened that using the semi-colon combinator instead of comma, 193 | writing `split ; linarith`. 194 | 195 | Next we will study a compressed version of that proof: 196 | -/ 197 | 198 | example {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) → y ≤ x := 199 | begin 200 | contrapose!, 201 | exact assume h, ⟨(y-x)/2, by linarith, by linarith⟩, 202 | end 203 | 204 | /- 205 | The angle brackets `⟨` and `⟩` introduce compound data or proofs. A proof 206 | of a `∃ z, P z` statemement is composed of a witness `z₀` and a proof `h` of 207 | `P z₀`. The compound is denoted by `⟨z₀, h⟩`. In the example above, the predicate is 208 | itself compound, it is a conjunction `P z ∧ Q z`. So the proof term should read 209 | `⟨z₀, ⟨h₁, h₂⟩⟩` where `h₁` (resp. `h₂`) is a proof of `P z₀` (resp. `Q z₀`). 210 | But these so-called "anonymous constructor" brackets are right-associative, so we can 211 | get rid of the nested brackets. 212 | 213 | The keyword `by` introduces tactic mode inside term mode, it is a shorter version 214 | of the `begin`/`end` pair, which is more convenient for single tactic blocks. 215 | In this example, `begin` enters tactic mode, `exact` leaves it, `by` re-enters it. 216 | 217 | Going all the way to a proof term would make the proof much longer, because we 218 | crucially use automation with `contrapose!` and `linarith`. We can still get a one-line 219 | proof using curly braces to gather several tactic invocations, and the `by` abbreviation 220 | instead of `begin`/`end`: 221 | -/ 222 | 223 | example {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) → y ≤ x := 224 | by { contrapose!, exact assume h, ⟨(y-x)/2, by linarith, by linarith⟩ } 225 | 226 | /- 227 | One could argue that the above proof is a bit too terse, and we are relying too much 228 | on linarith. Let's have more `linarith` calls for smaller steps. For the sake 229 | of (tiny) variation, we will also assume the premise and argue by contradiction 230 | instead of contraposing. 231 | -/ 232 | 233 | example {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) → y ≤ x := 234 | begin 235 | intro h, 236 | -- Assume the conclusion is false, and call this assumption H. 237 | by_contradiction H, 238 | push_neg at H, 239 | -- Now let's compute. 240 | have key := calc 241 | -- Each line must end with a colon followed by a proof term 242 | -- We want to specialize our assumption `h` to `ε = (y-x)/2` but this is long to 243 | -- type, so let's put a hole `_` that Lean will fill in by comparing the 244 | -- statement we want to prove and our proof term with a hole. As usual, 245 | -- positivity of `(y-x)/2` is proved by `linarith` 246 | y ≤ x + (y-x)/2 : h _ (by linarith) 247 | ... = x/2 + y/2 : by ring 248 | ... < y : by linarith, 249 | -- our key now says `y < y` (notice how the sequence `≤`, `=`, `<` was correctly 250 | -- merged into a `<`). Let `linarith` find the desired contradiction now. 251 | linarith, 252 | -- alternatively, we could have provided the proof term 253 | -- `exact lt_irrefl y key` 254 | end 255 | 256 | /- 257 | Now we are ready for some analysis. Let's define convergence of sequences of real numbers 258 | (of course there is a much more general definition in mathlib). 259 | -/ 260 | 261 | /-- The sequence `u` tends to `l` -/ 262 | def limit (u : ℕ → ℝ) (l : ℝ) := ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε 263 | 264 | /- 265 | In the above definition, `u n` denotes the n-th term of the sequence. We can 266 | add parentheses to get `u(n)` but we try to avoid parentheses because they pile up 267 | very quickly 268 | -/ 269 | 270 | -- If y ≤ u n for all n and u n goes to x then y ≤ x 271 | lemma le_lim {x y : ℝ} {u : ℕ → ℝ} (hu : limit u x) (ineq : ∀ n, y ≤ u n) : y ≤ x := 272 | begin 273 | -- Let's apply our previous lemma 274 | apply le_of_le_add_eps, 275 | -- We need to prove y ≤ x + ε for all positive ε. 276 | -- Let ε be any positive real 277 | intros ε ε_pos, 278 | -- we now specialize our limit assumption to this `ε`, and immediately 279 | -- fix a `N` as promised by the definition. 280 | cases hu ε ε_pos with N HN, 281 | -- Now we only need to compute until reaching the conclusion 282 | calc 283 | y ≤ u N : ineq N 284 | ... = x + (u N - x) : by linarith 285 | -- We'll need `add_le_add` which says `a ≤ b` and `c ≤ d` implies `a + c ≤ b + d` 286 | -- We need a lemma saying `z ≤ |z|`. Because we don't know the name of this lemma, 287 | -- let's use `library_search`. Because searching through the library is slow, 288 | -- Lean will write what it found in the Lean message window when cursor is on 289 | -- that line, so that we can replace it by the lemma. We see `le_abs_self`, which 290 | -- says `a ≤ |a|`, exactly what we're looking for. 291 | ... ≤ x + |u N - x| : add_le_add (by linarith) (by library_search) 292 | ... ≤ x + ε : add_le_add (by linarith) (HN N (by linarith)), 293 | end 294 | 295 | /- 296 | The next lemma has been extracted from the main proof in order to discuss numbers. 297 | In ordinary maths, we know that ℕ is *not* contained in `ℝ`, whatever the 298 | construction of real numbers that we use. For instance a natural number is not 299 | an equivalence class of Cauchy sequences. But it's very easy to 300 | pretend otherwise. Formal maths requires slightly more care. In the statement below, 301 | the "type ascription" `(n + 1 : ℝ)` forces Lean to convert the natural number 302 | `n+1` into a real number. The "inclusion" map will be displayed in tactic state 303 | as `↑`. There are various lemmas asserting this map is compatible with addition and 304 | monotone, but we don't want to bother writing their names. The `norm_cast` 305 | tactic is designed to wisely apply those lemmas for us. 306 | -/ 307 | 308 | lemma inv_succ_pos : ∀ n : ℕ, 1/(n+1 : ℝ) > 0 := 309 | begin 310 | -- Let `n` be any integer 311 | intro n, 312 | -- Since we don't know the name of the relevant lemma, asserting that the inverse of 313 | -- a positive number is positive, let's state that is suffices 314 | -- to prove that `n+1`, seen as a real number, is positive, and ask `library_search` 315 | suffices : (n + 1 : ℝ) > 0, 316 | { library_search }, 317 | -- Now we want to reduce to a statement about natural numbers, not real numbers 318 | -- coming from natural numbers. 319 | norm_cast, 320 | -- and then get the usual help from `linarith` 321 | linarith, 322 | end 323 | 324 | /- 325 | That was a pretty long proof for an obvious fact. And stating it as a lemma feels 326 | stupid, so let's find a way to write it on one line in case we want to include it 327 | in some other proof without stating a lemma. First the `library_search` call 328 | above displays the name of the relevant lemma: `one_div_pos`. We can also 329 | replace the `linarith` call on the last line by `library_search` to learn the name 330 | of the lemma `nat.succ_pos` asserting that the successor of a natural number is 331 | positive. There is also a variant on `norm_cast` that combines it with `exact`. 332 | The term mode analogue of `intro` is `λ`. We get down to: 333 | -/ 334 | 335 | example : ∀ n : ℕ, 1/(n+1 : ℝ) > 0 := 336 | λ n, one_div_pos.mpr (by exact_mod_cast nat.succ_pos n) 337 | 338 | /- 339 | The next proof uses mostly known things, so we will commment only new aspects. 340 | -/ 341 | 342 | lemma limit_inv_succ : ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, 1/(n + 1 : ℝ) ≤ ε := 343 | begin 344 | intros ε ε_pos, 345 | suffices : ∃ N : ℕ, 1/ε ≤ N, 346 | { -- Because we didn't provide a name for the above statement, Lean called it `this`. 347 | -- Let's fix an `N` that works. 348 | cases this with N HN, 349 | use N, 350 | intros n Hn, 351 | -- Now we want to rewrite the goal using lemmas 352 | -- `div_le_iff' : 0 < b → (a / b ≤ c ↔ a ≤ b * c)` 353 | -- `div_le_iff : 0 < b → (a / b ≤ c ↔ a ≤ c * b)` 354 | -- the second one will be rewritten from right to left, as indicated by `←`. 355 | -- Lean will create a side goal for the required positivity assumption that 356 | -- we don't provide for `div_le_iff'`. 357 | rw [div_le_iff', ← div_le_iff ε_pos], 358 | -- We want to replace assumption `Hn` by its real counter-part so that 359 | -- linarith can find what it needs. 360 | replace Hn : (N : ℝ) ≤ n, exact_mod_cast Hn, 361 | linarith, 362 | -- we are still left with the positivity assumption, but already discussed 363 | -- how to prove it in the preceding lemma 364 | exact_mod_cast nat.succ_pos n }, 365 | -- Now we need to prove that sufficient statement. 366 | -- We want to use that `ℝ` is archimedean. So we start typing 367 | -- `exact archimedean_` and hit Ctrl-space to see what completion Lean proposes 368 | -- the lemma `archimedean_iff_nat_le` sounds promising. We select the left to 369 | -- right implication using `.1`. This a generic lemma for fields equiped with 370 | -- a linear (ie total) order. We need to provide a proof that `ℝ` is indeed 371 | -- archimedean. This is done using the `apply_instance` tactic that will be 372 | -- covered elsewhere. 373 | exact archimedean_iff_nat_le.1 (by apply_instance) (1/ε), 374 | end 375 | 376 | /- 377 | We can now put all pieces together, with almost no new things to explain. 378 | -/ 379 | 380 | lemma inf_seq (A : set ℝ) (x : ℝ) : 381 | (x is_an_inf_of A) ↔ (x ∈ low_bounds A ∧ ∃ u : ℕ → ℝ, limit u x ∧ ∀ n, u n ∈ A ) := 382 | begin 383 | split, 384 | { intro h, 385 | split, 386 | { exact h.1 }, 387 | -- On the next line, we don't need to tell Lean to treat `n+1` as a real number because 388 | -- we add `x` to it, so Lean knows there is only one way to make sense of this expression. 389 | have key : ∀ n : ℕ, ∃ a ∈ A, a < x + 1/(n+1), 390 | { intro n, 391 | -- we can use the lemma we proved above 392 | apply inf_lt h, 393 | -- and another one we proved! 394 | have : 0 < 1/(n+1 : ℝ), from inv_succ_pos n, 395 | linarith }, 396 | -- Now we need to use axiom of (countable) choice 397 | choose u hu using key, 398 | use u, 399 | split, 400 | { intros ε ε_pos, 401 | -- again we use a lemma we proved, specializing it to our fixed `ε`, and fixing a `N` 402 | cases limit_inv_succ ε ε_pos with N H, 403 | use N, 404 | intros n hn, 405 | have : x ≤ u n, from h.1 _ (hu n).1, 406 | have := calc 407 | u n < x + 1/(n + 1) : (hu n).2 408 | ... ≤ x + ε : add_le_add (le_refl x) (H n hn), 409 | rw abs_of_nonneg ; linarith }, 410 | { intro n, 411 | exact (hu n).1 } }, 412 | { intro h, 413 | -- Assumption `h` is made of nested compound statements. We can use the 414 | -- recursive version of `cases` to unpack it in one go. 415 | rcases h with ⟨x_min, u, lim, huA⟩, 416 | split, 417 | exact x_min, 418 | intros y y_mino, 419 | apply le_lim lim, 420 | intro n, 421 | exact y_mino (u n) (huA n) }, 422 | end 423 | 424 | -------------------------------------------------------------------------------- /src/solutions/01_equality_rewriting.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | 3 | /- 4 | One of the earliest kind of proofs one encounters while learning mathematics is proving by 5 | a calculation. It may not sound like a proof, but this is actually using lemmas expressing 6 | properties of operations on numbers. It also uses the fundamental property of equality: if two 7 | mathematical objects A and B are equal then, in any statement involving A, one can replace A 8 | by B. This operation is called rewriting, and the Lean "tactic" for this is `rw`. 9 | 10 | In the following exercises, we will use the following two lemmas: 11 | mul_assoc a b c : a * b * c = a * (b * c) 12 | mul_comm a b : a*b = b*a 13 | 14 | Hence the command 15 | rw mul_assoc a b c, 16 | will replace a*b*c by a*(b*c) in the current goal. 17 | 18 | In order to replace backward, we use 19 | rw ← mul_assoc a b c, 20 | replacing a*(b*c) by a*b*c in the current goal. 21 | 22 | Of course we don't want to constantly invoke those lemmas, and we will eventually introduce 23 | more powerful solutions. 24 | -/ 25 | 26 | -- Uncomment the following line if you want to see parentheses around subexpressions. 27 | -- set_option pp.parens true 28 | 29 | example (a b c : ℝ) : (a * b) * c = b * (a * c) := 30 | begin 31 | rw mul_comm a b, 32 | rw mul_assoc b a c, 33 | end 34 | 35 | -- 0001 36 | example (a b c : ℝ) : (c * b) * a = b * (a * c) := 37 | begin 38 | -- sorry 39 | rw mul_comm c b, 40 | rw mul_assoc b c a, 41 | rw mul_comm c a, 42 | -- sorry 43 | end 44 | 45 | -- 0002 46 | example (a b c : ℝ) : a * (b * c) = b * (a * c) := 47 | begin 48 | -- sorry 49 | rw ← mul_assoc a b c, 50 | rw mul_comm a b, 51 | rw mul_assoc b a c, 52 | -- sorry 53 | end 54 | 55 | /- 56 | Now let's return to the preceding example to experiment with what happens 57 | if we don't give arguments to mul_assoc or mul_comm. 58 | For instance, you can start the next proof with 59 | rw ← mul_assoc, 60 | Try to figure out what happens. 61 | -/ 62 | 63 | -- 0003 64 | example (a b c : ℝ) : a * (b * c) = b * (a * c) := 65 | begin 66 | -- sorry 67 | rw ← mul_assoc, 68 | -- "rw mul_comm," doesn't do what we want. 69 | rw mul_comm a b, 70 | rw mul_assoc, 71 | -- sorry 72 | end 73 | 74 | /- 75 | We can also perform rewriting in an assumption of the local context, using for instance 76 | rw mul_comm a b at hyp, 77 | in order to replace a*b by b*a in assumption hyp. 78 | 79 | The next example will use a third lemma: 80 | two_mul a : 2*a = a + a 81 | 82 | Also we use the `exact` tactic, which allows to provide a direct proof term. 83 | -/ 84 | 85 | example (a b c d : ℝ) (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d := 86 | begin 87 | rw hyp' at hyp, 88 | rw mul_comm d a at hyp, 89 | rw ← two_mul (a*d) at hyp, 90 | rw ← mul_assoc 2 a d at hyp, 91 | exact hyp, -- Our assumption hyp is now exactly what we have to prove 92 | end 93 | 94 | /- 95 | And the next one can use: 96 | sub_self x : x - x = 0 97 | -/ 98 | 99 | -- 0004 100 | example (a b c d : ℝ) (hyp : c = b*a - d) (hyp' : d = a*b) : c = 0 := 101 | begin 102 | -- sorry 103 | rw hyp' at hyp, 104 | rw mul_comm b a at hyp, 105 | rw sub_self (a*b) at hyp, 106 | exact hyp, 107 | -- sorry 108 | end 109 | 110 | /- 111 | What is written in the two preceding example is very far away from what we would write on 112 | paper. Let's now see how to get a more natural layout. 113 | Inside each pair of curly braces below, the goal is to prove equality with the preceding line. 114 | -/ 115 | 116 | example (a b c d : ℝ) (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d := 117 | begin 118 | calc c = d*a + b : by { rw hyp } 119 | ... = d*a + a*d : by { rw hyp' } 120 | ... = a*d + a*d : by { rw mul_comm d a } 121 | ... = 2*(a*d) : by { rw two_mul } 122 | ... = 2*a*d : by { rw mul_assoc }, 123 | end 124 | 125 | /- 126 | Let's note there is no comma at the end of each line of calculation. `calc` is really one 127 | command, and the comma comes only after it's fully done. 128 | 129 | From a practical point of view, when writing such a proof, it is convenient to: 130 | * pause the tactic state view update in VScode by clicking the Pause icon button 131 | in the top right corner of the Lean Goal buffer 132 | * write the full calculation, ending each line with ": by {}" 133 | * resume tactic state update by clicking the Play icon button and fill in proofs between 134 | curly braces. 135 | 136 | Let's return to the other example using this method. 137 | -/ 138 | 139 | -- 0005 140 | example (a b c d : ℝ) (hyp : c = b*a - d) (hyp' : d = a*b) : c = 0 := 141 | begin 142 | -- sorry 143 | calc c = b*a - d : by { rw hyp } 144 | ... = b*a - a*b : by { rw hyp' } 145 | ... = a*b - a*b : by { rw mul_comm a b } 146 | ... = 0 : by { rw sub_self (a*b) }, 147 | -- sorry 148 | end 149 | 150 | /- 151 | The preceding proofs have exhausted our supply of "mul_comm" patience. Now it's time 152 | to get the computer to work harder. The `ring` tactic will prove any goal that follows by 153 | applying only the axioms of commutative (semi-)rings, in particular commutativity and 154 | associativity of addition and multiplication, as well as distributivity. 155 | 156 | We also note that curly braces are not necessary when we write a single tactic proof, so 157 | let's get rid of them. 158 | -/ 159 | 160 | example (a b c d : ℝ) (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d := 161 | begin 162 | calc c = d*a + b : by rw hyp 163 | ... = d*a + a*d : by rw hyp' 164 | ... = 2*a*d : by ring, 165 | end 166 | 167 | /- 168 | Of course we can use `ring` outside of `calc`. Let's do the next one in one line. 169 | -/ 170 | 171 | -- 0006 172 | example (a b c : ℝ) : a * (b * c) = b * (a * c) := 173 | begin 174 | -- sorry 175 | ring, 176 | -- sorry 177 | end 178 | 179 | /- 180 | This is too much fun. Let's do it again. 181 | -/ 182 | 183 | -- 0007 184 | example (a b : ℝ) : (a + b) + a = 2*a + b := 185 | begin 186 | -- sorry 187 | ring, 188 | -- sorry 189 | end 190 | 191 | /- 192 | Maybe this is cheating. Let's try to do the next computation without ring. 193 | We could use: 194 | pow_two x : x^2 = x*x 195 | mul_sub a b c : a*(b-c) = a*b - a*c 196 | add_mul a b c : (a+b)*c = a*c + b*c 197 | add_sub a b c : a + (b - c) = (a + b) - c 198 | sub_sub a b c : a - b - c = a - (b + c) 199 | add_zero a : a + 0 = a 200 | -/ 201 | 202 | -- 0008 203 | example (a b : ℝ) : (a + b)*(a - b) = a^2 - b^2 := 204 | begin 205 | -- sorry 206 | rw pow_two a, 207 | rw pow_two b, 208 | rw mul_sub (a+b) a b, 209 | rw add_mul a b a, 210 | rw add_mul a b b, 211 | rw mul_comm b a, 212 | rw ← sub_sub, 213 | rw ← add_sub, 214 | rw sub_self, 215 | rw add_zero, 216 | -- sorry 217 | end 218 | 219 | /- Let's stick to ring in the end. -/ 220 | -------------------------------------------------------------------------------- /src/solutions/02_iff_if_and.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | 3 | /- 4 | In the previous file, we saw how to rewrite using equalities. 5 | The analogue operation with mathematical statements is rewriting using 6 | equivalences. This is also done using the `rw` tactic. 7 | Lean uses ↔ to denote equivalence instead of ⇔. 8 | 9 | In the following exercises we will use the lemma: 10 | 11 | sub_nonneg {x y : ℝ} : 0 ≤ y - x ↔ x ≤ y 12 | 13 | The curly braces around x and y instead of parentheses mean Lean will always try to figure out what 14 | x and y are from context, unless we really insist on telling it (we'll see how to insist much later). 15 | Let's not worry about that for now. 16 | 17 | In order to announce an intermediate statement we use: 18 | 19 | have my_name : my statement, 20 | 21 | This triggers the apparition of a new goal: proving the statement. After this is done, 22 | the statement becomes available under the name `my_name`. 23 | We can focus on the current goal by typing tactics between curly braces. 24 | -/ 25 | 26 | example {a b c : ℝ} (hab : a ≤ b) : c + a ≤ c + b := 27 | begin 28 | rw ← sub_nonneg, 29 | have key : (c + b) - (c + a) = b - a, -- Here we introduce an intermediate statement named key 30 | { ring, }, -- and prove it between curly braces 31 | rw key, -- we can now use the key statement 32 | rw sub_nonneg, 33 | exact hab, 34 | end 35 | 36 | /- 37 | Of course the previous lemma is already in the core library, named `add_le_add_left`, so we can use it below. 38 | 39 | Let's prove a variation (without invoking commutativity of addition since this would spoil our fun). 40 | -/ 41 | 42 | -- 0009 43 | example {a b : ℝ} (hab : a ≤ b) (c : ℝ) : a + c ≤ b + c := 44 | begin 45 | -- sorry 46 | have key : (b + c) - (a + c) = b - a, 47 | { ring }, 48 | rw ← sub_nonneg, 49 | rw key, 50 | rw sub_nonneg, 51 | exact hab, 52 | -- sorry 53 | end 54 | 55 | 56 | /- 57 | Let's see how we could use this lemma. It is already in the core library, under the name `add_le_add_right`: 58 | 59 | add_le_add_right {a b : ℝ} (hab : a ≤ b) (c : ℝ) : a + c ≤ b + c 60 | 61 | This can be read as: "add_le_add_right is a function that will take as input real numbers a and b, an 62 | assumption `hab` claiming a ≤ b and a real number c, and will output a proof of a + c ≤ b + c". 63 | 64 | In addition, recall that curly braces around a b mean Lean will figure out those arguments unless we 65 | insist to help. This is because they can be deduced from the next argument `hab`. 66 | So it will be sufficient to feed `hab` and c to this function. 67 | -/ 68 | 69 | example {a b : ℝ} (ha : 0 ≤ a) : b ≤ a + b := 70 | begin 71 | calc b = 0 + b : by ring 72 | ... ≤ a + b : by exact add_le_add_right ha b, 73 | end 74 | 75 | /- 76 | In the second line of the above proof, we need to prove 0 + b ≤ a + b. 77 | The proof after the colon says: this is exactly lemma `add_le_add_right` applied to ha and b. 78 | Actually the `calc` block expects proof terms, and the `by` keyword is used to tell Lean we will use tactics 79 | to build such a proof term. But since the only tactic used in this block is `exact`, we can skip 80 | tactics entirely, and write: 81 | -/ 82 | 83 | example (a b : ℝ) (ha : 0 ≤ a) : b ≤ a + b := 84 | begin 85 | calc b = 0 + b : by ring 86 | ... ≤ a + b : add_le_add_right ha b, 87 | end 88 | 89 | /- Let's do a variant. -/ 90 | 91 | -- 0010 92 | example (a b : ℝ) (hb : 0 ≤ b) : a ≤ a + b := 93 | begin 94 | -- sorry 95 | calc a = a + 0 : by ring 96 | ... ≤ a + b : add_le_add_left hb a, 97 | -- sorry 98 | end 99 | 100 | /- 101 | The two preceding examples are in the core library : 102 | 103 | le_add_of_nonneg_left {a b : ℝ} (ha : 0 ≤ a) : b ≤ a + b 104 | le_add_of_nonneg_right {a b : ℝ} (hb : 0 ≤ b) : a ≤ a + b 105 | 106 | Again, there won't be any need to memorize those names, we will 107 | soon see how to get rid of such goals automatically. 108 | But we can already try to understand how their names are built: 109 | "le_add" describe the conclusion "less or equal than some addition" 110 | It comes first because we are focussed on proving stuff, and 111 | auto-completion works by looking at the beginning of words. 112 | "of" introduces assumptions. "nonneg" is Lean's abbreviation for non-negative. 113 | "left" or "right" disambiguates between the two variations. 114 | 115 | Let's use those lemmas by hand for now. 116 | 117 | Note that you can have several inequalities steps in a `calc` block, 118 | transitivity of inequalities will be used automatically to assemble 119 | the pieces. 120 | -/ 121 | 122 | -- 0011 123 | example (a b : ℝ) (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a + b := 124 | begin 125 | -- sorry 126 | calc 0 ≤ a : ha 127 | ... ≤ a + b : le_add_of_nonneg_right hb, 128 | -- sorry 129 | end 130 | 131 | /- And let's combine with our earlier lemmas. -/ 132 | 133 | -- 0012 134 | example (a b c d : ℝ) (hab : a ≤ b) (hcd : c ≤ d) : a + c ≤ b + d := 135 | begin 136 | -- sorry 137 | calc 138 | a + c ≤ b + c : add_le_add_right hab c 139 | ... ≤ b + d : add_le_add_left hcd b, 140 | -- sorry 141 | end 142 | 143 | /- 144 | In the above examples, we prepared proofs of assumptions of our lemmas beforehand, so 145 | that we could feed them to the lemmas. This is called forward reasoning. 146 | The `calc` proofs also belong to this category. 147 | 148 | We can also announce the use of a lemma, and provide proofs after the fact, using 149 | the `apply` tactic. This is called backward reasoning because we get the conclusion 150 | first, and provide proofs later. Using `rw` on the goal (rather than on an assumption 151 | from the local context) is also backward reasoning. 152 | 153 | Let's do that using the lemma 154 | 155 | mul_nonneg {x y : ℝ} (hx : 0 ≤ x) (hy : 0 ≤ y) : 0 ≤ x*y 156 | -/ 157 | 158 | example (a b c : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c := 159 | begin 160 | rw ← sub_nonneg, 161 | have key : b*c - a*c = (b - a)*c, 162 | { ring }, 163 | rw key, 164 | apply mul_nonneg, -- Here we don't provide proofs for the lemma's assumptions 165 | -- Now we need to provide the proofs. 166 | { rw sub_nonneg, 167 | exact hab }, 168 | { exact hc }, 169 | end 170 | 171 | /- 172 | Let's prove the same statement using only forward reasoning: announcing stuff, 173 | proving it by working with known facts, moving forward. 174 | -/ 175 | 176 | example (a b c : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c := 177 | begin 178 | have hab' : 0 ≤ b - a, 179 | { rw ← sub_nonneg at hab, 180 | exact hab, }, 181 | have h₁ : 0 ≤ (b - a)*c, 182 | { exact mul_nonneg hab' hc }, 183 | have h₂ : (b - a)*c = b*c - a*c, 184 | { ring, }, 185 | have h₃ : 0 ≤ b*c - a*c, 186 | { rw h₂ at h₁, 187 | exact h₁, }, 188 | rw sub_nonneg at h₃, 189 | exact h₃, 190 | end 191 | 192 | /- 193 | One reason why the backward reasoning proof is shorter is because Lean can 194 | infer of lot of things by comparing the goal and the lemma statement. Indeed 195 | in the `apply mul_nonneg` line, we didn't need to tell Lean that x = b - a 196 | and y = c in the lemma. It was infered by "unification" between the lemma 197 | statement and the goal. 198 | 199 | To be fair to the forward reasoning version, we should introduce a convenient 200 | variation on `rw`. The `rwa` tactic performs rewrite and then looks for an 201 | assumption matching the goal. We can use it to rewrite our latest proof as: 202 | -/ 203 | 204 | example (a b c : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c := 205 | begin 206 | have hab' : 0 ≤ b - a, 207 | { rwa ← sub_nonneg at hab, }, 208 | have h₁ : 0 ≤ (b - a)*c, 209 | { exact mul_nonneg hab' hc }, 210 | have h₂ : (b - a)*c = b*c - a*c, 211 | { ring, }, 212 | have h₃ : 0 ≤ b*c - a*c, 213 | { rwa h₂ at h₁, }, 214 | rwa sub_nonneg at h₃, 215 | end 216 | 217 | /- 218 | Let's now combine forward and backward reasoning, to get our most 219 | efficient proof of this statement. Note in particular how unification is used 220 | to know what to prove inside the parentheses in the `mul_nonneg` arguments. 221 | -/ 222 | example (a b c : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c := 223 | begin 224 | rw ← sub_nonneg, 225 | calc 0 ≤ (b - a)*c : mul_nonneg (by rwa sub_nonneg) hc 226 | ... = b*c - a*c : by ring, 227 | end 228 | 229 | /- 230 | Let's now practice all three styles using: 231 | 232 | mul_nonneg_of_nonpos_of_nonpos {a b : α} (ha : a ≤ 0) (hb : b ≤ 0) : 0 ≤ a * b 233 | 234 | sub_nonpos {a b : α} : a - b ≤ 0 ↔ a ≤ b 235 | -/ 236 | 237 | /- First using mostly backward reasoning -/ 238 | -- 0013 239 | example (a b c : ℝ) (hc : c ≤ 0) (hab : a ≤ b) : b*c ≤ a*c := 240 | begin 241 | -- sorry 242 | rw ← sub_nonneg, 243 | have fact : a*c - b*c = (a - b)*c, 244 | ring, 245 | rw fact, 246 | apply mul_nonneg_of_nonpos_of_nonpos, 247 | { rwa sub_nonpos }, 248 | { exact hc }, 249 | -- sorry 250 | end 251 | 252 | /- Using forward reasoning -/ 253 | -- 0014 254 | example (a b c : ℝ) (hc : c ≤ 0) (hab : a ≤ b) : b*c ≤ a*c := 255 | begin 256 | -- sorry 257 | have hab' : a - b ≤ 0, 258 | { rwa ← sub_nonpos at hab, }, 259 | have h₁ : 0 ≤ (a - b)*c, 260 | { exact mul_nonneg_of_nonpos_of_nonpos hab' hc }, 261 | have h₂ : (a - b)*c = a*c - b*c, 262 | { ring, }, 263 | have h₃ : 0 ≤ a*c - b*c, 264 | { rwa h₂ at h₁, }, 265 | rwa sub_nonneg at h₃, 266 | -- sorry 267 | end 268 | 269 | /-- Using a combination of both, with a `calc` block -/ 270 | -- 0015 271 | example (a b c : ℝ) (hc : c ≤ 0) (hab : a ≤ b) : b*c ≤ a*c := 272 | begin 273 | -- sorry 274 | have hab' : a - b ≤ 0, 275 | { rwa sub_nonpos }, 276 | rw ← sub_nonneg, 277 | calc 0 ≤ (a - b)*c : mul_nonneg_of_nonpos_of_nonpos hab' hc 278 | ... = a*c - b*c : by ring, 279 | -- sorry 280 | end 281 | 282 | /- 283 | Let's now move to proving implications. Lean denotes implications using 284 | a simple arrow →, the same it uses for functions (say denoting the type of functions 285 | from ℕ to ℕ by ℕ → ℕ). This is because it sees a proof of P ⇒ Q as a function turning 286 | a proof of P into a proof Q. 287 | 288 | Many of the examples that we already met are implications under the hood. For instance we proved 289 | 290 | le_add_of_nonneg_left (a b : ℝ) (ha : 0 ≤ a) : b ≤ a + b 291 | 292 | But this can be rephrased as 293 | 294 | le_add_of_nonneg_left (a b : ℝ) : 0 ≤ a → b ≤ a + b 295 | 296 | In order to prove P → Q, we use the tactic `intros`, followed by an assumption name. 297 | This creates an assumption with that name asserting that P holds, and turns the goal into Q. 298 | 299 | Let's check we can go from our old version of `le_add_of_nonneg_left` to the new one. 300 | 301 | -/ 302 | example (a b : ℝ): 0 ≤ a → b ≤ a + b := 303 | begin 304 | intros ha, 305 | exact le_add_of_nonneg_left ha, 306 | end 307 | 308 | /- 309 | Actually Lean doesn't make any difference between those two versions. It is also happy with 310 | -/ 311 | example (a b : ℝ): 0 ≤ a → b ≤ a + b := 312 | le_add_of_nonneg_left 313 | 314 | /- No tactic state is shown in the above line because we don't even need to enter 315 | tactic mode using `begin` or `by`. 316 | 317 | Let's practise using `intros`. -/ 318 | 319 | -- 0016 320 | example (a b : ℝ): 0 ≤ b → a ≤ a + b := 321 | begin 322 | -- sorry 323 | intros hb, 324 | calc a = a + 0 : by ring 325 | ... ≤ a + b : add_le_add_left hb a, 326 | -- sorry 327 | end 328 | 329 | 330 | 331 | /- 332 | What about lemmas having more than one assumption? For instance: 333 | 334 | add_nonneg {a b : ℝ} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a + b 335 | 336 | A natural idea is to use the conjunction operator (logical AND), which Lean denotes 337 | by ∧. Assumptions built using this operator can be decomposed using the `cases` tactic, 338 | which is a very general assumption-decomposing tactic. 339 | -/ 340 | example {a b : ℝ} : (0 ≤ a ∧ 0 ≤ b) → 0 ≤ a + b := 341 | begin 342 | intros hyp, 343 | cases hyp with ha hb, 344 | exact add_nonneg ha hb, 345 | end 346 | 347 | /- 348 | Needing that intermediate line invoking `cases` shows this formulation is not what is used by 349 | Lean. It rather sees `add_nonneg` as two nested implications: 350 | if a is non-negative then if b is non-negative then a+b is non-negative. 351 | It reads funny, but it is much more convenient to use in practice. 352 | -/ 353 | example {a b : ℝ} : 0 ≤ a → (0 ≤ b → 0 ≤ a + b) := 354 | add_nonneg 355 | 356 | /- 357 | The above pattern is so common that implications are defined as right-associative operators, 358 | hence parentheses are not needed above. 359 | 360 | Let's prove that the naive conjunction version implies the funny Lean version. For this we need 361 | to know how to prove a conjunction. The `split` tactic creates two goals from a conjunction goal. 362 | It can also be used to create two implication goals from an equivalence goal. 363 | -/ 364 | example {a b : ℝ} (H : (0 ≤ a ∧ 0 ≤ b) → 0 ≤ a + b) : 0 ≤ a → (0 ≤ b → 0 ≤ a + b) := 365 | begin 366 | intros ha, 367 | intros hb, 368 | apply H, 369 | split, 370 | exact ha, 371 | exact hb, 372 | end 373 | 374 | /- 375 | Let's practice `cases` and `split`. In the next exercise, P, Q and R denote 376 | unspecified mathematical statements. 377 | -/ 378 | 379 | -- 0017 380 | example (P Q R : Prop) : P ∧ Q → Q ∧ P := 381 | begin 382 | -- sorry 383 | intro hyp, 384 | cases hyp with hP hQ, 385 | split, 386 | exact hQ, 387 | exact hP, 388 | -- sorry 389 | end 390 | 391 | /- 392 | Of course using `split` only to be able to use `exact` twice in a row feels silly. One can 393 | also use the anonymous constructor syntax: ⟨ ⟩ 394 | Beware those are not parentheses but angle brackets. You can type them as \< and \> in VS 395 | Code. They are a generic way of providing compound objects to Lean when Lean already has a 396 | very clear idea of what it is waiting for. 397 | 398 | So we could have replaced the last three lines by: 399 | exact ⟨hQ, hP⟩ 400 | 401 | We can also combine the `intros` steps. We can now compress our earlier proof to: 402 | -/ 403 | 404 | example {a b : ℝ} (H : (0 ≤ a ∧ 0 ≤ b) → 0 ≤ a + b) : 0 ≤ a → (0 ≤ b → 0 ≤ a + b) := 405 | begin 406 | intros ha hb, 407 | exact H ⟨ha, hb⟩, 408 | end 409 | 410 | /- 411 | The anonymous contructor trick actually also works in `intros` provided we use 412 | its recursive version `rintros`. So we can replace 413 | intro h, 414 | cases h with h₁ h₂ 415 | by 416 | rintros ⟨h₁, h₂⟩, 417 | Now redo the previous exercise using all those compressing techniques, in exactly two lines. -/ 418 | 419 | -- 0018 420 | example (P Q R : Prop): P ∧ Q → Q ∧ P := 421 | begin 422 | -- sorry 423 | rintros ⟨hP, hQ⟩, 424 | exact ⟨hQ, hP⟩, 425 | -- sorry 426 | end 427 | 428 | /- 429 | We are ready to come back to the equivalence between the different formulations of 430 | lemmas having two assumptions. Remember the `split` tactic can be used to split 431 | an equivalence into two implications. 432 | -/ 433 | 434 | -- 0019 435 | example (P Q R : Prop) : (P ∧ Q → R) ↔ (P → (Q → R)) := 436 | begin 437 | -- sorry 438 | split, 439 | { intros hyp hP hQ, 440 | exact hyp ⟨hP, hQ⟩ }, 441 | { rintro hyp ⟨hP, hQ⟩, 442 | exact hyp hP hQ }, 443 | -- sorry 444 | end 445 | 446 | /- 447 | If you used more than five lines in the above exercise then try to compress things 448 | (without simply removing line ends). 449 | 450 | One last compression technique: given a proof h of a conjunction P ∧ Q, one can get 451 | a proof of P using h.left and a proof of Q using h.right, without using cases. 452 | One can also use the more generic (but less legible) names h.1 and h.2. 453 | 454 | Similarly, given a proof h of P ↔ Q, one can get a proof of P → Q using h.mp 455 | and a proof of Q → P using h.mpr (or the generic h.1 and h.2 that are even less legible 456 | in this case). 457 | 458 | Before the final exercise in this file, let's make sure we'll be able to leave 459 | without learning 10 lemma names. The `linarith` tactic will prove any equality or 460 | inequality or contradiction that follows by linear combinations of assumptions from the 461 | context (with constant coefficients). 462 | -/ 463 | 464 | example (a b : ℝ) (hb : 0 ≤ b) : a ≤ a + b := 465 | begin 466 | linarith, 467 | end 468 | 469 | /- 470 | Now let's enjoy this for a while. 471 | -/ 472 | 473 | -- 0020 474 | example (a b : ℝ) (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a + b := 475 | begin 476 | -- sorry 477 | linarith, 478 | -- sorry 479 | end 480 | 481 | /- And let's combine with our earlier lemmas. -/ 482 | 483 | -- 0021 484 | example (a b c d : ℝ) (hab : a ≤ b) (hcd : c ≤ d) : a + c ≤ b + d := 485 | begin 486 | -- sorry 487 | linarith, 488 | -- sorry 489 | end 490 | 491 | 492 | /- 493 | Final exercise 494 | 495 | In the last exercise of this file, we will use the divisibility relation on ℕ, 496 | denoted by ∣ (beware this is a unicode divisibility bar, not the ASCII pipe character), 497 | and the gcd function. 498 | 499 | The definitions are the usual ones, but our goal is to avoid using these definitions and 500 | only use the following three lemmas: 501 | 502 | dvd_refl (a : ℕ) : a ∣ a 503 | 504 | dvd_antisymm {a b : ℕ} : a ∣ b → b ∣ a → a = b := 505 | 506 | dvd_gcd_iff {a b c : ℕ} : c ∣ gcd a b ↔ c ∣ a ∧ c ∣ b 507 | -/ 508 | 509 | -- All functions and lemmas below are about natural numbers. 510 | open nat 511 | 512 | -- 0022 513 | example (a b : ℕ) : a ∣ b ↔ gcd a b = a := 514 | begin 515 | -- sorry 516 | have fact : gcd a b ∣ a ∧ gcd a b ∣ b, 517 | { rw ← dvd_gcd_iff }, 518 | split, 519 | { intro h, 520 | apply dvd_antisymm fact.left, 521 | rw dvd_gcd_iff, 522 | exact ⟨dvd_refl a, h⟩ }, 523 | { intro h, 524 | rw ← h, 525 | exact fact.right }, 526 | -- sorry 527 | end 528 | -------------------------------------------------------------------------------- /src/solutions/03_forall_or.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | import algebra.group.pi 3 | 4 | set_option pp.beta true 5 | 6 | /- 7 | In this file, we'll learn about the ∀ quantifier, and the disjunction 8 | operator ∨ (logical OR). 9 | 10 | Let P be a predicate on a type X. This means for every mathematical 11 | object x with type X, we get a mathematical statement P x. 12 | In Lean, P x has type Prop. 13 | 14 | Lean sees a proof h of `∀ x, P x` as a function sending any `x : X` to 15 | a proof `h x` of `P x`. 16 | This already explains the main way to use an assumption or lemma which 17 | starts with a ∀. 18 | 19 | In order to prove `∀ x, P x`, we use `intros x` to fix an arbitrary object 20 | with type X, and call it x. 21 | 22 | Note also we don't need to give the type of x in the expression `∀ x, P x` 23 | as long as the type of P is clear to Lean, which can then infer the type of x. 24 | 25 | Let's define two predicates to play with ∀. 26 | -/ 27 | 28 | def even_fun (f : ℝ → ℝ) := ∀ x, f (-x) = f x 29 | 30 | def odd_fun (f : ℝ → ℝ) := ∀ x, f (-x) = -f x 31 | 32 | /- 33 | In the next proof, we also take the opportunity to introduce the 34 | `unfold` tactic, which simply unfolds definitions. Here this is purely 35 | for didactic reason, Lean doesn't need those `unfold` invocations. 36 | We will also use `rfl` which is a term proving equalities that are true 37 | by definition (in a very strong sense to be discussed later). 38 | -/ 39 | 40 | example (f g : ℝ → ℝ) : even_fun f → even_fun g → even_fun (f + g) := 41 | begin 42 | -- Assume f is even 43 | intros hf, 44 | -- which means ∀ x, f (-x) = f x 45 | unfold even_fun at hf, 46 | -- and the same for g 47 | intros hg, 48 | unfold even_fun at hg, 49 | -- We need to prove ∀ x, (f+g)(-x) = (f+g)(x) 50 | unfold even_fun, 51 | -- Let x be any real number 52 | intros x, 53 | -- and let's compute 54 | calc (f + g) (-x) = f (-x) + g (-x) : rfl 55 | ... = f x + g (-x) : by rw hf x 56 | ... = f x + g x : by rw hg x 57 | ... = (f + g) x : rfl 58 | end 59 | 60 | /- 61 | In the preceding proof, all `unfold` lines are purely for 62 | psychological comfort. 63 | 64 | Sometimes unfolding is necessary because we want to apply a tactic 65 | that operates purely on the syntactical level. 66 | The main such tactic is `rw`. 67 | 68 | The same property of `rw` explain why the first computation line 69 | is necessary, although its proof is simply `rfl`. 70 | Before that line, `rw hf x` won't find anything like `f (-x)` hence 71 | will give up. 72 | The last line is not necessary however, since it only proves 73 | something that is true by definition, and is not followed by 74 | a `rw`. 75 | 76 | Also, Lean doesn't need to be told that hf should be specialized to 77 | x before rewriting, exactly as in the first file 01_equality_rewriting. 78 | We can also gather several rewrites using a list of expressions. 79 | 80 | One last trick is that `rw` can take a list of expressions to use for 81 | rewriting. For instance `rw [h₁, h₂, h₃]` is equivalent to three 82 | lines `rw h₁`, `rw h₂` and `rw h₃`. Note that you can inspect the tactic 83 | state between those rewrites when reading a proof using this syntax. You 84 | simply need to move the cursor inside the list. 85 | 86 | Hence we can compress the above proof to: 87 | -/ 88 | 89 | example (f g : ℝ → ℝ) : even_fun f → even_fun g → even_fun (f + g) := 90 | begin 91 | intros hf hg x, 92 | calc (f + g) (-x) = f (-x) + g (-x) : rfl 93 | ... = f x + g x : by rw [hf, hg] 94 | end 95 | 96 | /- 97 | Now let's practice. If you need to learn how to type a unicode symbol, 98 | you can put your mouse cursor above the symbol and wait for one second. 99 | -/ 100 | 101 | -- 0023 102 | example (f g : ℝ → ℝ) : even_fun f → even_fun (g ∘ f) := 103 | begin 104 | -- sorry 105 | intros hf x, 106 | calc (g ∘ f) (-x) = g (f (-x)) : rfl 107 | ... = g (f x) : by rw hf 108 | -- sorry 109 | end 110 | 111 | -- 0024 112 | example (f g : ℝ → ℝ) : odd_fun f → odd_fun g → odd_fun (g ∘ f) := 113 | begin 114 | -- sorry 115 | intros hf hg x, 116 | calc (g ∘ f) (-x) = g (f (-x)) : rfl 117 | ... = - (g ∘ f) x : by rw [hf, hg], 118 | -- sorry 119 | end 120 | 121 | /- 122 | Let's have more quantifiers, and play with forward and backward reasoning. 123 | 124 | In the next definitions, note how `∀ x₁, ∀ x₂` is abreviated to `∀ x₁ x₂`. 125 | -/ 126 | 127 | def non_decreasing (f : ℝ → ℝ) := ∀ x₁ x₂, x₁ ≤ x₂ → f x₁ ≤ f x₂ 128 | 129 | def non_increasing (f : ℝ → ℝ) := ∀ x₁ x₂, x₁ ≤ x₂ → f x₁ ≥ f x₂ 130 | 131 | /- Let's be very explicit and use forward reasoning first. -/ 132 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) := 133 | begin 134 | -- Let x₁ and x₂ be real numbers such that x₁ ≤ x₂ 135 | intros x₁ x₂ h, 136 | -- Since f is non-decreasing, f x₁ ≤ f x₂. 137 | have step₁ : f x₁ ≤ f x₂, 138 | exact hf x₁ x₂ h, 139 | -- Since g is non-decreasing, we then get g (f x₁) ≤ g (f x₂). 140 | exact hg (f x₁) (f x₂) step₁, 141 | end 142 | 143 | /- 144 | In the above proof, note how inconvenient it is to specify x₁ and x₂ in `hf x₁ x₂ h` since 145 | they could be inferred from the type of h. 146 | We could have written `hf _ _ h` and Lean would have filled the holes denoted by _. 147 | 148 | Even better we could have written the definition 149 | of `non_decreasing` as: ∀ {x₁ x₂}, x₁ ≤ x₂ → f x₁ ≤ f x₂, with curly braces to denote 150 | implicit arguments. 151 | 152 | But let's leave that aside for now. One possible variation on the above proof is to 153 | use the `specialize` tactic to replace hf by its specialization to the relevant value. 154 | -/ 155 | 156 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) := 157 | begin 158 | intros x₁ x₂ h, 159 | specialize hf x₁ x₂ h, 160 | exact hg (f x₁) (f x₂) hf, 161 | end 162 | 163 | /- 164 | This `specialize` tactic is mostly useful for exploration, or in preparation for rewriting 165 | in the assumption. One can very often replace its use by using more complicated expressions 166 | directly involving the original assumption, as in the next variation: 167 | -/ 168 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) := 169 | begin 170 | intros x₁ x₂ h, 171 | exact hg (f x₁) (f x₂) (hf x₁ x₂ h), 172 | end 173 | 174 | /- 175 | Since the above proof uses only `intros` and `exact`, we could very easily replace it by the 176 | raw proof term: 177 | -/ 178 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) := 179 | λ x₁ x₂ h, hg (f x₁) (f x₂) (hf x₁ x₂ h) 180 | 181 | /- 182 | Of course the above proof is difficult to decipher. The principle in mathlib is to use 183 | such a proof when the result is obvious and you don't want to read the proof anyway. 184 | 185 | Instead of pursuing this style, let's see how backward reasoning would look like here. 186 | As usual with this style, we use `apply` and enjoy Lean specializing assumptions for us 187 | using unification. 188 | -/ 189 | 190 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) := 191 | begin 192 | -- Let x₁ and x₂ be real numbers such that x₁ ≤ x₂ 193 | intros x₁ x₂ h, 194 | -- We need to prove (g ∘ f) x₁ ≤ (g ∘ f) x₂. 195 | -- Since g is non-decreasing, it suffices to prove f x₁ ≤ f x₂ 196 | apply hg, 197 | -- which follows from our assumption on f 198 | apply hf, 199 | -- and on x₁ and x₂ 200 | exact h 201 | end 202 | 203 | -- 0025 204 | example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_increasing g) : non_increasing (g ∘ f) := 205 | begin 206 | -- sorry 207 | intros x₁ x₂ h, 208 | apply hg, 209 | exact hf x₁ x₂ h 210 | -- sorry 211 | end 212 | 213 | /- 214 | Let's switch to disjunctions now. Lean denotes by ∨ the 215 | logical OR operator. 216 | 217 | In order to make use of an assumption 218 | hyp : P ∨ Q 219 | we use the cases tactic: 220 | cases hyp with hP hQ 221 | which creates two proof branches: one branch assuming hP : P, 222 | and one branch assuming hQ : Q. 223 | 224 | In order to directly prove a goal P ∨ Q, 225 | we use either the `left` tactic and prove P or the `right` 226 | tactic and prove Q. 227 | 228 | In the next proof we use `ring` and `linarith` to get rid of 229 | easy computations or inequalities, as well as one lemma: 230 | 231 | mul_eq_zero : a*b = 0 ↔ a = 0 ∨ b = 0 232 | -/ 233 | 234 | example (a b : ℝ) : a = a*b → a = 0 ∨ b = 1 := 235 | begin 236 | intro hyp, 237 | have H : a*(1 - b) = 0, 238 | { calc a*(1 - b) = a - a*b : by ring 239 | ... = 0 : by linarith, }, 240 | rw mul_eq_zero at H, 241 | cases H with Ha Hb, 242 | { left, 243 | exact Ha, }, 244 | { right, 245 | linarith, }, 246 | end 247 | 248 | -- 0026 249 | example (x y : ℝ) : x^2 = y^2 → x = y ∨ x = -y := 250 | begin 251 | -- sorry 252 | intros hyp, 253 | have H : (x-y)*(x+y) = 0, 254 | calc (x-y)*(x+y) = x^2 - y^2 : by ring 255 | ... = y^2 - y^2 : by rw hyp 256 | ... = 0 : by ring, 257 | rw mul_eq_zero at H, 258 | cases H with h1 h2, 259 | { left, 260 | linarith, }, 261 | { right, 262 | linarith, }, 263 | -- sorry 264 | end 265 | 266 | /- 267 | In the next exercise, we can use: 268 | eq_or_lt_of_le : x ≤ y → x = y ∨ x < y 269 | -/ 270 | 271 | -- 0027 272 | example (f : ℝ → ℝ) : non_decreasing f ↔ ∀ x y, x < y → f x ≤ f y := 273 | begin 274 | -- sorry 275 | split, 276 | { intros hf x y hxy, 277 | apply hf, 278 | linarith, }, 279 | { intros hf x y hxy, 280 | have clef : x = y ∨ x < y, 281 | { exact eq_or_lt_of_le hxy }, 282 | cases clef with hxy hxy, 283 | rw hxy, 284 | exact hf x y hxy, }, 285 | -- sorry 286 | end 287 | 288 | /- 289 | In the next exercise, we can use: 290 | le_total x y : x ≤ y ∨ y ≤ x 291 | -/ 292 | 293 | -- 0028 294 | example (f : ℝ → ℝ) (h : non_decreasing f) (h' : ∀ x, f (f x) = x) : ∀ x, f x = x := 295 | begin 296 | -- sorry 297 | intro x, 298 | have : f (f x) = x, 299 | { rw h' }, 300 | have : (f x ≤ x) ∨ (x ≤ f x), 301 | { exact le_total (f x) x }, 302 | cases this with hx hx, 303 | { have f1: f (f x) ≤ f x, 304 | { exact h (f x) x hx, }, 305 | rw h' at f1, 306 | linarith, }, 307 | { have f1: f x ≤ f (f x), 308 | { exact h x (f x) hx, }, 309 | rw h' x at f1, 310 | linarith, }, 311 | -- sorry 312 | end 313 | -------------------------------------------------------------------------------- /src/solutions/04_exists.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | import data.int.parity 3 | 4 | /- 5 | In this file, we learn how to handle the ∃ quantifier. 6 | 7 | In order to prove `∃ x, P x`, we give some x₀ using tactic `use x₀` and 8 | then prove `P x₀`. This x₀ can be an object from the local context 9 | or a more complicated expression. 10 | -/ 11 | example : ∃ n : ℕ, 8 = 2*n := 12 | begin 13 | use 4, 14 | refl, -- this is the tactic analogue of the rfl proof term 15 | end 16 | 17 | /- 18 | In order to use `h : ∃ x, P x`, we use the `cases` tactic to fix 19 | one x₀ that works. 20 | 21 | Again h can come straight from the local context or can be a more 22 | complicated expression. 23 | -/ 24 | example (n : ℕ) (h : ∃ k : ℕ, n = k + 1) : n > 0 := 25 | begin 26 | -- Let's fix k₀ such that n = k₀ + 1. 27 | cases h with k₀ hk₀, 28 | -- It now suffices to prove k₀ + 1 > 0. 29 | rw hk₀, 30 | -- and we have a lemma about this 31 | exact nat.succ_pos k₀, 32 | end 33 | 34 | /- 35 | The next exercises use divisibility in ℤ (beware the ∣ symbol which is 36 | not ASCII). 37 | 38 | By definition, a ∣ b ↔ ∃ k, b = a*k, so you can prove a ∣ b using the 39 | `use` tactic. 40 | -/ 41 | 42 | -- Until the end of this file, a, b and c will denote integers, unless 43 | -- explicitly stated otherwise 44 | variables (a b c : ℤ) 45 | 46 | -- 0029 47 | example (h₁ : a ∣ b) (h₂ : b ∣ c) : a ∣ c := 48 | begin 49 | -- sorry 50 | cases h₁ with k hk, 51 | cases h₂ with l hl, 52 | use k*l, 53 | calc c = b*l : hl 54 | ... = (a*k)*l : by rw hk 55 | ... = a*(k*l) : by ring, 56 | -- sorry 57 | end 58 | 59 | /- 60 | A very common pattern is to have an assumption or lemma asserting 61 | h : ∃ x, y = ... 62 | and this is used through the combo: 63 | cases h with x hx, 64 | rw hx at ... 65 | The tactic `rcases` allows us to do recursive `cases`, as indicated by its name, 66 | and also simplifies the above combo when the name hx is replaced by the special 67 | name `rfl`, as in the following example. 68 | It uses the anonymous constructor angle brackets syntax. 69 | -/ 70 | 71 | 72 | example (h1 : a ∣ b) (h2 : a ∣ c) : a ∣ b+c := 73 | begin 74 | rcases h1 with ⟨k, rfl⟩, 75 | rcases h2 with ⟨l, rfl⟩, 76 | use k+l, 77 | ring, 78 | end 79 | 80 | /- 81 | You can use the same `rfl` trick with the `rintros` tactic. 82 | -/ 83 | 84 | example : a ∣ b → a ∣ c → a ∣ b+c := 85 | begin 86 | rintros ⟨k, rfl⟩ ⟨l, rfl⟩, 87 | use k+l, 88 | ring, 89 | end 90 | 91 | -- 0030 92 | example : 0 ∣ a ↔ a = 0 := 93 | begin 94 | -- sorry 95 | split, 96 | { rintro ⟨k, rfl⟩, 97 | ring, }, 98 | { rintro rfl, 99 | use 0, 100 | refl, }, 101 | -- sorry 102 | end 103 | 104 | /- 105 | We can now start combining quantifiers, using the definition 106 | 107 | surjective (f : X → Y) := ∀ y, ∃ x, f x = y 108 | 109 | -/ 110 | open function 111 | 112 | -- In the remaining of this file, f and g will denote functions from 113 | -- ℝ to ℝ. 114 | variables (f g : ℝ → ℝ) 115 | 116 | 117 | -- 0031 118 | example (h : surjective (g ∘ f)) : surjective g := 119 | begin 120 | -- sorry 121 | intro y, 122 | rcases h y with ⟨w, rfl⟩, 123 | use f w, 124 | -- sorry 125 | end 126 | 127 | /- 128 | The above exercise can be done in three lines. Try to do the 129 | next exercise in four lines. 130 | -/ 131 | 132 | -- 0032 133 | example (hf : surjective f) (hg : surjective g) : surjective (g ∘ f) := 134 | begin 135 | -- sorry 136 | intro z, 137 | rcases hg z with ⟨y, rfl⟩, 138 | rcases hf y with ⟨x, rfl⟩, 139 | use x, 140 | -- sorry 141 | end 142 | -------------------------------------------------------------------------------- /src/solutions/05_sequence_limits.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | import algebra.group.pi 3 | import tuto_lib 4 | 5 | notation `|`x`|` := abs x 6 | 7 | /- 8 | In this file we manipulate the elementary definition of limits of 9 | sequences of real numbers. 10 | mathlib has a much more general definition of limits, but here 11 | we want to practice using the logical operators and relations 12 | covered in the previous files. 13 | 14 | A sequence u is a function from ℕ to ℝ, hence Lean says 15 | u : ℕ → ℝ 16 | The definition we'll be using is: 17 | 18 | -- Definition of « u tends to l » 19 | def seq_limit (u : ℕ → ℝ) (l : ℝ) : Prop := 20 | ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε 21 | 22 | Note the use of `∀ ε > 0, ...` which is an abbreviation of 23 | `∀ ε, ε > 0 → ... ` 24 | 25 | In particular, a statement like `h : ∀ ε > 0, ...` 26 | can be specialized to a given ε₀ by 27 | `specialize h ε₀ hε₀` 28 | where hε₀ is a proof of ε₀ > 0. 29 | 30 | Also recall that, wherever Lean expects some proof term, we can 31 | start a tactic mode proof using the keyword `by` (followed by curly braces 32 | if you need more than one tactic invocation). 33 | For instance, if the local context contains: 34 | 35 | δ : ℝ 36 | δ_pos : δ > 0 37 | h : ∀ ε > 0, ... 38 | 39 | then we can specialize h to the real number δ/2 using: 40 | `specialize h (δ/2) (by linarith)` 41 | where `by linarith` will provide the proof of `δ/2 > 0` expected by Lean. 42 | 43 | We'll take this opportunity to use two new tactics: 44 | 45 | `norm_num` will perform numerical normalization on the goal and `norm_num at h` 46 | will do the same in assumption `h`. This will get rid of trivial calculations on numbers, 47 | like replacing |l - l| by zero in the next exercise. 48 | 49 | `congr'` will try to prove equalities between applications of functions by recursively 50 | proving the arguments are the same. 51 | For instance, if the goal is `f x + g y = f z + g t` then congr will replace it by 52 | two goals: `x = z` and `y = t`. 53 | You can limit the recursion depth by specifying a natural number after `congr'`. 54 | For instance, in the above example, `congr' 1` will give new goals 55 | `f x = f z` and `g y = g t`, which only inspect arguments of the addition and not deeper. 56 | -/ 57 | 58 | variables (u v w : ℕ → ℝ) (l l' : ℝ) 59 | 60 | -- If u is constant with value l then u tends to l 61 | -- 0033 62 | example : (∀ n, u n = l) → seq_limit u l := 63 | begin 64 | -- sorry 65 | intros h ε ε_pos, 66 | use 0, 67 | intros n hn, 68 | rw h, 69 | norm_num, 70 | linarith, 71 | -- sorry 72 | end 73 | 74 | /- When dealing with absolute values, we'll use lemmas: 75 | 76 | abs_le {x y : ℝ} : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y 77 | 78 | abs_add (x y : ℝ) : |x + y| ≤ |x| + |y| 79 | 80 | abs_sub_comm (x y : ℝ) : |x - y| = |y - x| 81 | 82 | You should probably write them down on a sheet of paper that you keep at 83 | hand since they are used in many exercises. 84 | -/ 85 | 86 | -- Assume l > 0. Then u tends to l implies u n ≥ l/2 for large enough n 87 | -- 0034 88 | example (hl : l > 0) : seq_limit u l → ∃ N, ∀ n ≥ N, u n ≥ l/2 := 89 | begin 90 | -- sorry 91 | intro h, 92 | cases h (l/2) (by linarith) with N hN, 93 | use N, 94 | intros n hn, 95 | specialize hN n hn, 96 | rw abs_le at hN, 97 | linarith, 98 | -- sorry 99 | end 100 | 101 | /- 102 | When dealing with max, you can use 103 | 104 | ge_max_iff (p q r) : r ≥ max p q ↔ r ≥ p ∧ r ≥ q 105 | 106 | le_max_left p q : p ≤ max p q 107 | 108 | le_max_right p q : q ≤ max p q 109 | 110 | You should probably add them to the sheet of paper where you wrote 111 | the `abs` lemmas since they are used in many exercises. 112 | 113 | Let's see an example. 114 | -/ 115 | 116 | -- If u tends to l and v tends l' then u+v tends to l+l' 117 | example (hu : seq_limit u l) (hv : seq_limit v l') : 118 | seq_limit (u + v) (l + l') := 119 | begin 120 | intros ε ε_pos, 121 | cases hu (ε/2) (by linarith) with N₁ hN₁, 122 | cases hv (ε/2) (by linarith) with N₂ hN₂, 123 | use max N₁ N₂, 124 | intros n hn, 125 | cases ge_max_iff.mp hn with hn₁ hn₂, 126 | have fact₁ : |u n - l| ≤ ε/2, 127 | from hN₁ n (by linarith), -- note the use of `from`. 128 | -- This is an alias for `exact`, 129 | -- but reads nicer in this context 130 | have fact₂ : |v n - l'| ≤ ε/2, 131 | from hN₂ n (by linarith), 132 | calc 133 | |(u + v) n - (l + l')| = |u n + v n - (l + l')| : rfl 134 | ... = |(u n - l) + (v n - l')| : by congr' 1 ; ring 135 | ... ≤ |u n - l| + |v n - l'| : by apply abs_add 136 | ... ≤ ε : by linarith, 137 | end 138 | 139 | /- 140 | In the above proof, we used `have` to prepare facts for `linarith` consumption in the last line. 141 | Since we have direct proof terms for them, we can feed them directly to `linarith` as in the next proof 142 | of the same statement. 143 | Another variation we introduce is rewriting using `ge_max_iff` and letting `linarith` handle the 144 | conjunction, instead of creating two new assumptions. 145 | -/ 146 | 147 | example (hu : seq_limit u l) (hv : seq_limit v l') : 148 | seq_limit (u + v) (l + l') := 149 | begin 150 | intros ε ε_pos, 151 | cases hu (ε/2) (by linarith) with N₁ hN₁, 152 | cases hv (ε/2) (by linarith) with N₂ hN₂, 153 | use max N₁ N₂, 154 | intros n hn, 155 | rw ge_max_iff at hn, 156 | calc 157 | |(u + v) n - (l + l')| = |u n + v n - (l + l')| : rfl 158 | ... = |(u n - l) + (v n - l')| : by congr' 1 ; ring 159 | ... ≤ |u n - l| + |v n - l'| : by apply abs_add 160 | ... ≤ ε : by linarith [hN₁ n (by linarith), hN₂ n (by linarith)], 161 | end 162 | 163 | /- Let's do something similar: the squeezing theorem. -/ 164 | -- 0035 165 | example (hu : seq_limit u l) (hw : seq_limit w l) 166 | (h : ∀ n, u n ≤ v n) 167 | (h' : ∀ n, v n ≤ w n) : seq_limit v l := 168 | begin 169 | -- sorry 170 | intros ε ε_pos, 171 | cases hu ε ε_pos with N hN, 172 | cases hw ε ε_pos with N' hN', 173 | use max N N', 174 | intros n hn, 175 | rw ge_max_iff at hn, 176 | specialize hN n (by linarith), 177 | specialize hN' n (by linarith), 178 | specialize h n, 179 | specialize h' n, 180 | rw abs_le at *, 181 | split, 182 | -- Here `linarith` can finish, but on paper we would write 183 | calc -ε ≤ u n - l : by linarith 184 | ... ≤ v n - l : by linarith, 185 | calc v n - l ≤ w n - l : by linarith 186 | ... ≤ ε : by linarith, 187 | -- sorry 188 | 189 | end 190 | 191 | /- What about < ε? -/ 192 | -- 0036 193 | example (u l) : seq_limit u l ↔ 194 | ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| < ε := 195 | begin 196 | -- sorry 197 | split, 198 | { intros hyp ε ε_pos, 199 | cases hyp (ε/2) (by linarith) with N hN, 200 | use N, 201 | intros n hn, 202 | calc |u n - l| ≤ ε/2 : by exact hN n hn 203 | ... < ε : by linarith, }, 204 | { intros hyp ε ε_pos, 205 | cases hyp ε ε_pos with N hN, 206 | use N, 207 | intros n hn, 208 | specialize hN n hn, 209 | linarith, }, 210 | -- sorry 211 | end 212 | 213 | /- In the next exercise, we'll use 214 | 215 | eq_of_abs_sub_le_all (x y : ℝ) : (∀ ε > 0, |x - y| ≤ ε) → x = y 216 | -/ 217 | 218 | -- A sequence admits at most one limit 219 | -- 0037 220 | example : seq_limit u l → seq_limit u l' → l = l' := 221 | begin 222 | -- sorry 223 | intros hl hl', 224 | apply eq_of_abs_sub_le_all, 225 | intros ε ε_pos, 226 | cases hl (ε/2) (by linarith) with N hN, 227 | cases hl' (ε/2) (by linarith) with N' hN', 228 | calc |l - l'| = |(l-u (max N N')) + (u (max N N') -l')| : by ring_nf 229 | ... ≤ |l - u (max N N')| + |u (max N N') - l'| : by apply abs_add 230 | ... = |u (max N N') - l| + |u (max N N') - l'| : by rw abs_sub_comm 231 | ... ≤ ε : by linarith [hN (max N N') (le_max_left _ _), hN' (max N N') (le_max_right _ _)] 232 | -- sorry 233 | end 234 | 235 | /- 236 | Let's now practice deciphering definitions before proving. 237 | -/ 238 | 239 | def non_decreasing (u : ℕ → ℝ) := ∀ n m, n ≤ m → u n ≤ u m 240 | 241 | def is_seq_sup (M : ℝ) (u : ℕ → ℝ) := 242 | (∀ n, u n ≤ M) ∧ ∀ ε > 0, ∃ n₀, u n₀ ≥ M - ε 243 | 244 | -- 0038 245 | example (M : ℝ) (h : is_seq_sup M u) (h' : non_decreasing u) : 246 | seq_limit u M := 247 | begin 248 | -- sorry 249 | intros ε ε_pos, 250 | cases h with inf_M sup_M_ep, 251 | cases sup_M_ep ε ε_pos with n₀ hn₀, 252 | use n₀, 253 | intros n hn, 254 | rw abs_le, 255 | split; linarith [inf_M n, h' n₀ n hn], 256 | -- sorry 257 | end 258 | -------------------------------------------------------------------------------- /src/solutions/06_sub_sequences.lean: -------------------------------------------------------------------------------- 1 | import tuto_lib 2 | /- 3 | This file continues the elementary study of limits of sequences. 4 | It can be skipped if the previous file was too easy, it won't introduce 5 | any new tactic or trick. 6 | 7 | Remember useful lemmas: 8 | 9 | abs_le {x y : ℝ} : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y 10 | 11 | abs_add (x y : ℝ) : |x + y| ≤ |x| + |y| 12 | 13 | abs_sub_comm (x y : ℝ) : |x - y| = |y - x| 14 | 15 | ge_max_iff (p q r) : r ≥ max p q ↔ r ≥ p ∧ r ≥ q 16 | 17 | le_max_left p q : p ≤ max p q 18 | 19 | le_max_right p q : q ≤ max p q 20 | 21 | and the definition: 22 | 23 | def seq_limit (u : ℕ → ℝ) (l : ℝ) : Prop := 24 | ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε 25 | 26 | You can also use a property proved in the previous file: 27 | 28 | unique_limit : seq_limit u l → seq_limit u l' → l = l' 29 | 30 | def extraction (φ : ℕ → ℕ) := ∀ n m, n < m → φ n < φ m 31 | -/ 32 | 33 | 34 | variable { φ : ℕ → ℕ} 35 | 36 | /- 37 | The next lemma is proved by an easy induction, but we haven't seen induction 38 | in this tutorial. If you did the natural number game then you can delete 39 | the proof below and try to reconstruct it. 40 | -/ 41 | /-- An extraction is greater than id -/ 42 | lemma id_le_extraction' : extraction φ → ∀ n, n ≤ φ n := 43 | begin 44 | intros hyp n, 45 | induction n with n hn, 46 | { exact nat.zero_le _ }, 47 | { exact nat.succ_le_of_lt (by linarith [hyp n (n+1) (by linarith)]) }, 48 | end 49 | 50 | /-- Extractions take arbitrarily large values for arbitrarily large 51 | inputs. -/ 52 | -- 0039 53 | lemma extraction_ge : extraction φ → ∀ N N', ∃ n ≥ N', φ n ≥ N := 54 | begin 55 | -- sorry 56 | intros h N N', 57 | use max N N', 58 | split, 59 | apply le_max_right, 60 | calc 61 | N ≤ max N N' : by apply le_max_left 62 | ... ≤ φ (max N N') : by apply id_le_extraction' h 63 | -- sorry 64 | end 65 | 66 | /-- A real number `a` is a cluster point of a sequence `u` 67 | if `u` has a subsequence converging to `a`. 68 | 69 | def cluster_point (u : ℕ → ℝ) (a : ℝ) := 70 | ∃ φ, extraction φ ∧ seq_limit (u ∘ φ) a 71 | -/ 72 | 73 | variables {u : ℕ → ℝ} {a l : ℝ} 74 | 75 | /- 76 | In the exercise, we use `∃ n ≥ N, ...` which is the abbreviation of 77 | `∃ n, n ≥ N ∧ ...`. 78 | Lean can read this abbreviation, but displays it as the confusing: 79 | `∃ (n : ℕ) (H : n ≥ N)` 80 | One gets used to it. Alternatively, one can get rid of it using the lemma 81 | exists_prop {p q : Prop} : (∃ (h : p), q) ↔ p ∧ q 82 | -/ 83 | 84 | /-- If `a` is a cluster point of `u` then there are values of 85 | `u` arbitrarily close to `a` for arbitrarily large input. -/ 86 | -- 0040 87 | lemma near_cluster : 88 | cluster_point u a → ∀ ε > 0, ∀ N, ∃ n ≥ N, |u n - a| ≤ ε := 89 | begin 90 | -- sorry 91 | intros hyp ε ε_pos N, 92 | rcases hyp with ⟨φ, φ_extr, hφ⟩, 93 | cases hφ ε ε_pos with N' hN', 94 | rcases extraction_ge φ_extr N N' with ⟨q, hq, hq'⟩, 95 | exact ⟨φ q, hq', hN' _ hq⟩, 96 | -- sorry 97 | end 98 | 99 | /- 100 | The above exercice can be done in five lines. 101 | Hint: you can use the anonymous constructor syntax when proving 102 | existential statements. 103 | -/ 104 | 105 | /-- If `u` tends to `l` then its subsequences tend to `l`. -/ 106 | -- 0041 107 | lemma subseq_tendsto_of_tendsto' (h : seq_limit u l) (hφ : extraction φ) : 108 | seq_limit (u ∘ φ) l := 109 | begin 110 | -- sorry 111 | intros ε ε_pos, 112 | cases h ε ε_pos with N hN, 113 | use N, 114 | intros n hn, 115 | apply hN, 116 | calc N ≤ n : hn 117 | ... ≤ φ n : id_le_extraction' hφ n, 118 | -- sorry 119 | end 120 | 121 | /-- If `u` tends to `l` all its cluster points are equal to `l`. -/ 122 | -- 0042 123 | lemma cluster_limit (hl : seq_limit u l) (ha : cluster_point u a) : a = l := 124 | begin 125 | -- sorry 126 | rcases ha with ⟨φ, φ_extr, lim_u_φ⟩, 127 | have lim_u_φ' : seq_limit (u ∘ φ) l, 128 | from subseq_tendsto_of_tendsto' hl φ_extr, 129 | exact unique_limit lim_u_φ lim_u_φ', 130 | -- sorry 131 | end 132 | 133 | /-- Cauchy_sequence sequence -/ 134 | def cauchy_sequence (u : ℕ → ℝ) := ∀ ε > 0, ∃ N, ∀ p q, p ≥ N → q ≥ N → |u p - u q| ≤ ε 135 | 136 | -- 0043 137 | example : (∃ l, seq_limit u l) → cauchy_sequence u := 138 | begin 139 | -- sorry 140 | intro hyp, 141 | cases hyp with l hl, 142 | intros ε ε_pos, 143 | cases hl (ε/2) (by linarith) with N hN, 144 | use N, 145 | intros p q hp hq, 146 | calc |u p - u q| = |(u p - l) + (l - u q)| : by ring_nf 147 | ... ≤ |u p - l| + |l - u q| : by apply abs_add 148 | ... = |u p - l| + |u q - l| : by rw abs_sub_comm (u q) l 149 | ... ≤ ε : by linarith [hN p hp, hN q hq], 150 | -- sorry 151 | end 152 | 153 | 154 | /- 155 | In the next exercise, you can reuse 156 | near_cluster : cluster_point u a → ∀ ε > 0, ∀ N, ∃ n ≥ N, |u n - a| ≤ ε 157 | -/ 158 | -- 0044 159 | example (hu : cauchy_sequence u) (hl : cluster_point u l) : seq_limit u l := 160 | begin 161 | -- sorry 162 | intros ε ε_pos, 163 | cases hu (ε/2) (by linarith) with N hN, 164 | use N, 165 | have clef : ∃ N' ≥ N, |u N' - l| ≤ ε/2, 166 | apply near_cluster hl (ε/2) (by linarith), 167 | cases clef with N' h, 168 | cases h with hNN' hN', 169 | intros n hn, 170 | calc |u n - l| = |(u n - u N') + (u N' - l)| : by ring 171 | ... ≤ |u n - u N'| + |u N' - l| : by apply abs_add 172 | ... ≤ ε : by linarith [hN n N' (by linarith) hNN'], 173 | -- sorry 174 | end 175 | -------------------------------------------------------------------------------- /src/solutions/07_first_negations.lean: -------------------------------------------------------------------------------- 1 | import tuto_lib 2 | import data.int.parity 3 | /- 4 | Negations, proof by contradiction and contraposition. 5 | 6 | This file introduces the logical rules and tactics related to negation: 7 | exfalso, by_contradiction, contrapose, by_cases and push_neg. 8 | 9 | There is a special statement denoted by `false` which, by definition, 10 | has no proof. 11 | 12 | So `false` implies everything. Indeed `false → P` means any proof of 13 | `false` could be turned into a proof of P. 14 | This fact is known by its latin name 15 | "ex falso quod libet" (from false follows whatever you want). 16 | Hence Lean's tactic to invoke this is called `exfalso`. 17 | -/ 18 | 19 | example : false → 0 = 1 := 20 | begin 21 | intro h, 22 | exfalso, 23 | exact h, 24 | end 25 | 26 | /- 27 | The preceding example suggests that this definition of `false` isn't very useful. 28 | But actually it allows us to define the negation of a statement P as 29 | "P implies false" that we can read as "if P were true, we would get 30 | a contradiction". Lean denotes this by `¬ P`. 31 | 32 | One can prove that (¬ P) ↔ (P ↔ false). But in practice we directly 33 | use the definition of `¬ P`. 34 | -/ 35 | 36 | example {x : ℝ} : ¬ x < x := 37 | begin 38 | intro hyp, 39 | rw lt_iff_le_and_ne at hyp, 40 | cases hyp with hyp_inf hyp_non, 41 | clear hyp_inf, -- we won't use that one, so let's discard it 42 | change x = x → false at hyp_non, -- Lean doesn't need this psychological line 43 | apply hyp_non, 44 | refl, 45 | end 46 | 47 | open int 48 | 49 | -- 0045 50 | example (n : ℤ) (h_even : even n) (h_not_even : ¬ even n) : 0 = 1 := 51 | begin 52 | -- sorry 53 | exfalso, 54 | exact h_not_even h_even, 55 | -- sorry 56 | end 57 | 58 | -- 0046 59 | example (P Q : Prop) (h₁ : P ∨ Q) (h₂ : ¬ (P ∧ Q)) : ¬ P ↔ Q := 60 | begin 61 | -- sorry 62 | split, 63 | { intro hnP, 64 | cases h₁ with hP hQ, 65 | { exfalso, 66 | exact hnP hP, }, 67 | { exact hQ }, }, 68 | { intros hQ hP, 69 | exact h₂ ⟨hP, hQ⟩ }, 70 | -- sorry 71 | end 72 | 73 | /- 74 | The definition of negation easily implies that, for every statement P, 75 | P → ¬ ¬ P 76 | 77 | The excluded middle axiom, which asserts P ∨ ¬ P allows us to 78 | prove the converse implication. 79 | 80 | Together those two implications form the principle of double negation elimination. 81 | not_not {P : Prop} : (¬ ¬ P) ↔ P 82 | 83 | The implication `¬ ¬ P → P` is the basis for proofs by contradiction: 84 | in order to prove P, it suffices to prove ¬¬ P, ie `¬ P → false`. 85 | 86 | Of course there is no need to keep explaining all this. The tactic 87 | `by_contradiction Hyp` will transform any goal P into `false` and 88 | add Hyp : ¬ P to the local context. 89 | 90 | Let's return to a proof from the 5th file: uniqueness of limits for a sequence. 91 | This cannot be proved without using some version of the excluded middle 92 | axiom. We used it secretely in 93 | 94 | eq_of_abs_sub_le_all (x y : ℝ) : (∀ ε > 0, |x - y| ≤ ε) → x = y 95 | 96 | (we'll prove a variation on this lemma below). 97 | 98 | In the proof below, we also take the opportunity to introduce the `let` tactic 99 | which creates a local definition. If needed, it can be unfolded using `dsimp` which 100 | takes a list of definitions to unfold. For instance after using `let N₀ := max N N'`, 101 | you could write `dsimp [N₀] at h` to replace `N₀` by its definition in some 102 | local assumption `h`. 103 | -/ 104 | example (u : ℕ → ℝ) (l l' : ℝ) : seq_limit u l → seq_limit u l' → l = l' := 105 | begin 106 | intros hl hl', 107 | by_contradiction H, 108 | change l ≠ l' at H, -- Lean does not need this line 109 | have ineg : |l-l'| > 0, 110 | exact abs_pos.mpr (sub_ne_zero_of_ne H), -- details about that line are not important 111 | cases hl ( |l-l'|/4 ) (by linarith) with N hN, 112 | cases hl' ( |l-l'|/4 ) (by linarith) with N' hN', 113 | let N₀ := max N N', 114 | specialize hN N₀ (le_max_left _ _), 115 | specialize hN' N₀ (le_max_right _ _), 116 | have clef : |l-l'| < |l-l'|, 117 | calc 118 | |l - l'| = |(l-u N₀) + (u N₀ -l')| : by ring_nf 119 | ... ≤ |l - u N₀| + |u N₀ - l'| : by apply abs_add 120 | ... = |u N₀ - l| + |u N₀ - l'| : by rw abs_sub_comm 121 | ... < |l-l'| : by linarith, 122 | linarith, -- linarith can also find simple numerical contradictions 123 | end 124 | 125 | /- 126 | Another incarnation of the excluded middle axiom is the principle of 127 | contraposition: in order to prove P ⇒ Q, it suffices to prove 128 | non Q ⇒ non P. 129 | -/ 130 | 131 | -- Using a proof by contradiction, let's prove the contraposition principle 132 | -- 0047 133 | example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q := 134 | begin 135 | -- sorry 136 | intro hP, 137 | by_contradiction hnQ, 138 | exact h hnQ hP, 139 | -- sorry 140 | end 141 | 142 | /- 143 | Again Lean doesn't need this principle explained to it. We can use the 144 | `contrapose` tactic. 145 | -/ 146 | 147 | example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q := 148 | begin 149 | contrapose, 150 | exact h, 151 | end 152 | 153 | /- 154 | In the next exercise, we'll use 155 | odd n : ∃ k, n = 2*k + 1 156 | int.odd_iff_not_even {n : ℤ} : odd n ↔ ¬ even n 157 | -/ 158 | -- 0048 159 | example (n : ℤ) : even (n^2) ↔ even n := 160 | begin 161 | -- sorry 162 | split, 163 | { contrapose, 164 | rw ← int.odd_iff_not_even, 165 | rw ← int.odd_iff_not_even, 166 | rintro ⟨k, rfl⟩, 167 | use 2*k*(k+1), 168 | ring }, 169 | { rintro ⟨k, rfl⟩, 170 | use 2*k^2, 171 | ring }, 172 | -- sorry 173 | end 174 | /- 175 | As a last step on our law of the excluded middle tour, let's notice that, especially 176 | in pure logic exercises, it can sometimes be useful to use the 177 | excluded middle axiom in its original form: 178 | classical.em : ∀ P, P ∨ ¬ P 179 | 180 | Instead of applying this lemma and then using the `cases` tactic, we 181 | have the shortcut 182 | by_cases h : P, 183 | 184 | combining both steps to create two proof branches: one assuming 185 | h : P, and the other assuming h : ¬ P 186 | 187 | For instance, let's prove a reformulation of this implication relation, 188 | which is sometimes used as a definition in other logical foundations, 189 | especially those based on truth tables (hence very strongly using 190 | excluded middle from the very beginning). 191 | -/ 192 | 193 | variables (P Q : Prop) 194 | 195 | example : (P → Q) ↔ (¬ P ∨ Q) := 196 | begin 197 | split, 198 | { intro h, 199 | by_cases hP : P, 200 | { right, 201 | exact h hP }, 202 | { left, 203 | exact hP } }, 204 | { intros h hP, 205 | cases h with hnP hQ, 206 | { exfalso, 207 | exact hnP hP }, 208 | { exact hQ } }, 209 | end 210 | 211 | -- 0049 212 | example : ¬ (P ∧ Q) ↔ ¬ P ∨ ¬ Q := 213 | begin 214 | -- sorry 215 | split, 216 | { intro h, 217 | by_cases hP : P, 218 | { right, 219 | intro hQ, 220 | exact h ⟨hP, hQ⟩ }, 221 | { left, 222 | exact hP } }, 223 | { rintros h ⟨hP, hQ⟩, 224 | cases h with hnP hnQ, 225 | { exact hnP hP }, 226 | { exact hnQ hQ } }, 227 | -- sorry 228 | end 229 | 230 | /- 231 | It is crucial to understand negation of quantifiers. 232 | Let's do it by hand for a little while. 233 | In the first exercise, only the definition of negation is needed. 234 | -/ 235 | 236 | -- 0050 237 | example (n : ℤ) : ¬ (∃ k, n = 2*k) ↔ ∀ k, n ≠ 2*k := 238 | begin 239 | -- sorry 240 | split, 241 | { intros hyp k hk, 242 | exact hyp ⟨k, hk⟩ }, 243 | { rintros hyp ⟨k, rfl⟩, 244 | exact hyp k rfl }, 245 | -- sorry 246 | end 247 | 248 | /- 249 | Contrary to negation of the existential quantifier, negation of the 250 | universal quantifier requires excluded middle for the first implication. 251 | In order to prove this, we can use either 252 | * a double proof by contradiction 253 | * a contraposition, not_not : (¬ ¬ P) ↔ P) and a proof by contradiction. 254 | -/ 255 | 256 | def even_fun (f : ℝ → ℝ) := ∀ x, f (-x) = f x 257 | 258 | -- 0051 259 | example (f : ℝ → ℝ) : ¬ even_fun f ↔ ∃ x, f (-x) ≠ f x := 260 | begin 261 | -- sorry 262 | split, 263 | { contrapose, 264 | intro h, 265 | rw not_not, 266 | intro x, 267 | by_contradiction H, 268 | apply h, 269 | use x, 270 | /- Alternative version 271 | intro h, 272 | by_contradiction H, 273 | apply h, 274 | intro x, 275 | by_contradiction H', 276 | apply H, 277 | use x, -/ }, 278 | { rintros ⟨x, hx⟩ h', 279 | exact hx (h' x) }, 280 | -- sorry 281 | end 282 | 283 | /- 284 | Of course we can't keep repeating the above proofs, especially the second one. 285 | So we use the `push_neg` tactic. 286 | -/ 287 | 288 | example : ¬ even_fun (λ x, 2*x) := 289 | begin 290 | unfold even_fun, -- Here unfolding is important because push_neg won't do it. 291 | push_neg, 292 | use 42, 293 | linarith, 294 | end 295 | 296 | -- 0052 297 | example (f : ℝ → ℝ) : ¬ even_fun f ↔ ∃ x, f (-x) ≠ f x := 298 | begin 299 | -- sorry 300 | unfold even_fun, 301 | push_neg, 302 | -- sorry 303 | end 304 | 305 | def bounded_above (f : ℝ → ℝ) := ∃ M, ∀ x, f x ≤ M 306 | 307 | example : ¬ bounded_above (λ x, x) := 308 | begin 309 | unfold bounded_above, 310 | push_neg, 311 | intro M, 312 | use M + 1, 313 | linarith, 314 | end 315 | 316 | -- Let's contrapose 317 | -- 0053 318 | example (x : ℝ) : (∀ ε > 0, x ≤ ε) → x ≤ 0 := 319 | begin 320 | -- sorry 321 | contrapose, 322 | push_neg, 323 | intro h, 324 | use x/2, 325 | split ; linarith, 326 | -- sorry 327 | end 328 | 329 | /- 330 | The "contrapose, push_neg" combo is so common that we can abreviate it to 331 | `contrapose!` 332 | 333 | Let's use this trick, together with: 334 | eq_or_lt_of_le : a ≤ b → a = b ∨ a < b 335 | -/ 336 | 337 | -- 0054 338 | example (f : ℝ → ℝ) : (∀ x y, x < y → f x < f y) ↔ (∀ x y, (x ≤ y ↔ f x ≤ f y)) := 339 | begin 340 | -- sorry 341 | split, 342 | { intros hf x y, 343 | split, 344 | { intros hxy, 345 | cases eq_or_lt_of_le hxy with hxy hxy, 346 | { rw hxy }, 347 | { linarith [hf x y hxy]} }, 348 | { contrapose!, 349 | apply hf } }, 350 | { intros hf x y, 351 | contrapose!, 352 | intro h, 353 | rwa hf, } 354 | -- sorry 355 | end 356 | -------------------------------------------------------------------------------- /src/solutions/07bis_abstract_negations.lean: -------------------------------------------------------------------------------- 1 | import data.real.basic 2 | 3 | open_locale classical 4 | 5 | /- 6 | Theoretical negations. 7 | 8 | This file is for people interested in logic who want to fully understand 9 | negations. 10 | 11 | Here we don't use `contrapose` or `push_neg`. The goal is to prove lemmas 12 | that are used by those tactics. Of course we can use 13 | `exfalso`, `by_contradiction` and `by_cases`. 14 | 15 | If this doesn't sound like fun then skip ahead to the next file. 16 | -/ 17 | 18 | section negation_prop 19 | 20 | variables P Q : Prop 21 | 22 | -- 0055 23 | example : (P → Q) ↔ (¬ Q → ¬ P) := 24 | begin 25 | -- sorry 26 | split, 27 | { intros h hnQ hP, 28 | exact hnQ (h hP) }, 29 | { intros h hP, 30 | by_contradiction hnQ, 31 | exact h hnQ hP }, 32 | -- sorry 33 | end 34 | 35 | -- 0056 36 | lemma non_imp (P Q : Prop) : ¬ (P → Q) ↔ P ∧ ¬ Q := 37 | begin 38 | -- sorry 39 | split, 40 | { intro h, 41 | by_contradiction H, 42 | apply h, 43 | intro hP, 44 | by_contradiction H', 45 | apply H, 46 | exact ⟨hP, H'⟩ }, 47 | { intros h h', 48 | cases h with hP hnQ, 49 | exact hnQ (h' hP) }, 50 | -- sorry 51 | end 52 | 53 | -- In the next one, let's use the axiom 54 | -- propext {P Q : Prop} : (P ↔ Q) → P = Q 55 | 56 | -- 0057 57 | example (P : Prop) : ¬ P ↔ P = false := 58 | begin 59 | -- sorry 60 | split, 61 | { intro h, 62 | apply propext, 63 | split, 64 | { intro h', 65 | exact h h' }, 66 | { intro h, 67 | exfalso, 68 | exact h } }, 69 | { intro h, 70 | rw h, 71 | exact id }, 72 | -- sorry 73 | end 74 | 75 | end negation_prop 76 | 77 | section negation_quantifiers 78 | variables (X : Type) (P : X → Prop) 79 | 80 | -- 0058 81 | example : ¬ (∀ x, P x) ↔ ∃ x, ¬ P x := 82 | begin 83 | -- sorry 84 | split, 85 | { intro h, 86 | by_contradiction H, 87 | apply h, 88 | intros x, 89 | by_contradiction H', 90 | apply H, 91 | use [x, H'] }, 92 | { rintros ⟨x, hx⟩ h', 93 | exact hx (h' x) }, 94 | -- sorry 95 | end 96 | 97 | -- 0059 98 | example : ¬ (∃ x, P x) ↔ ∀ x, ¬ P x := 99 | begin 100 | -- sorry 101 | split, 102 | { intros h x h', 103 | apply h, 104 | use [x, h'] }, 105 | { rintros h ⟨x, hx⟩, 106 | exact h x hx }, 107 | -- sorry 108 | end 109 | 110 | -- 0060 111 | example (P : ℝ → Prop) : ¬ (∃ ε > 0, P ε) ↔ ∀ ε > 0, ¬ P ε := 112 | begin 113 | -- sorry 114 | split, 115 | { intros h ε ε_pos hP, 116 | apply h, 117 | use [ε, ε_pos, hP] }, 118 | { rintros h ⟨ε, ε_pos, hP⟩, 119 | exact h ε ε_pos hP }, 120 | -- sorry 121 | end 122 | 123 | -- 0061 124 | example (P : ℝ → Prop) : ¬ (∀ x > 0, P x) ↔ ∃ x > 0, ¬ P x := 125 | begin 126 | -- sorry 127 | split, 128 | { intros h, 129 | by_contradiction H, 130 | apply h, 131 | intros x x_pos, 132 | by_contradiction HP, 133 | apply H, 134 | use [x, x_pos, HP] }, 135 | { rintros ⟨x, xpos, hx⟩ h', 136 | exact hx (h' x xpos) }, 137 | -- sorry 138 | end 139 | 140 | end negation_quantifiers 141 | -------------------------------------------------------------------------------- /src/solutions/08_limits_negation.lean: -------------------------------------------------------------------------------- 1 | import tuto_lib 2 | 3 | 4 | section 5 | /- 6 | The first part of this file makes sure you can negate quantified statements 7 | in your head without the help of `push_neg`. 8 | 9 | You need to complete the statement and then use the `check_me` tactic 10 | to check your answer. This tactic exists only for those exercises, 11 | it mostly calls `push_neg` and then cleans up a bit. 12 | 13 | def seq_limit (u : ℕ → ℝ) (l : ℝ) : Prop := 14 | ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε 15 | -/ 16 | 17 | -- In this section, u denotes a sequence of real numbers 18 | -- f is a function from ℝ to ℝ 19 | -- x₀ and l are real numbers 20 | variables (u : ℕ → ℝ) (f : ℝ → ℝ) (x₀ l : ℝ) 21 | 22 | /- Negation of "u tends to l" -/ 23 | -- 0062 24 | example : ¬ (∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε) ↔ 25 | -- sorry 26 | ∃ ε > 0, ∀ N, ∃ n ≥ N, |u n - l| > ε 27 | -- sorry 28 | := 29 | begin 30 | -- sorry 31 | check_me, 32 | -- sorry 33 | end 34 | 35 | /- Negation of "f is continuous at x₀" -/ 36 | -- 0063 37 | example : ¬ (∀ ε > 0, ∃ δ > 0, ∀ x, |x - x₀| ≤ δ → |f x - f x₀| ≤ ε) ↔ 38 | -- sorry 39 | ∃ ε > 0, ∀ δ > 0, ∃ x, |x - x₀| ≤ δ ∧ |f x - f x₀| > ε 40 | -- sorry 41 | := 42 | begin 43 | -- sorry 44 | check_me, 45 | -- sorry 46 | end 47 | 48 | /- 49 | In the next exercise, we need to keep in mind that 50 | `∀ x x', ...` is the abbreviation of 51 | `∀ x, ∀ x', ... `. 52 | 53 | Also, `∃ x x', ...` is the abbreviation of `∃ x, ∃ x', ...`. 54 | -/ 55 | 56 | /- Negation of "f is uniformly continuous on ℝ" -/ 57 | -- 0064 58 | example : ¬ (∀ ε > 0, ∃ δ > 0, ∀ x x', |x' - x| ≤ δ → |f x' - f x| ≤ ε) ↔ 59 | -- sorry 60 | ∃ ε > 0, ∀ δ > 0, ∃ x x', |x' - x| ≤ δ ∧ |f x' - f x| > ε 61 | -- sorry 62 | := 63 | begin 64 | -- sorry 65 | check_me, 66 | -- sorry 67 | end 68 | 69 | /- Negation of "f is sequentially continuous at x₀" -/ 70 | -- 0065 71 | example : ¬ (∀ u : ℕ → ℝ, (∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - x₀| ≤ ε) → (∀ ε > 0, ∃ N, ∀ n ≥ N, |(f ∘ u) n - f x₀| ≤ ε)) ↔ 72 | -- sorry 73 | ∃ u : ℕ → ℝ, 74 | (∀ δ > 0, ∃ N, ∀ n ≥ N, |u n - x₀| ≤ δ) ∧ 75 | (∃ ε > 0, ∀ N, ∃ n ≥ N, |f (u n) - f x₀| > ε) 76 | -- sorry 77 | := 78 | begin 79 | -- sorry 80 | check_me, 81 | -- sorry 82 | end 83 | end 84 | 85 | /- 86 | We now turn to elementary applications of negations to limits of sequences. 87 | Remember that `linarith` can find easy numerical contradictions. 88 | 89 | Also recall the following lemmas: 90 | 91 | abs_le {x y : ℝ} : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y 92 | 93 | ge_max_iff (p q r) : r ≥ max p q ↔ r ≥ p ∧ r ≥ q 94 | 95 | le_max_left p q : p ≤ max p q 96 | 97 | le_max_right p q : q ≤ max p q 98 | 99 | /-- The sequence `u` tends to `+∞`. -/ 100 | def tendsto_infinity (u : ℕ → ℝ) := ∀ A, ∃ N, ∀ n ≥ N, u n ≥ A 101 | -/ 102 | 103 | -- 0066 104 | example {u : ℕ → ℝ} : tendsto_infinity u → ∀ l, ¬ seq_limit u l := 105 | begin 106 | -- sorry 107 | intros lim_infinie l lim_l, 108 | cases lim_l 1 (by linarith) with N hN, 109 | cases lim_infinie (l+2) with N' hN', 110 | let N₀ := max N N', 111 | specialize hN N₀ (le_max_left _ _), 112 | specialize hN' N₀ (le_max_right _ _), 113 | rw abs_le at hN, 114 | linarith, 115 | -- sorry 116 | end 117 | 118 | def nondecreasing_seq (u : ℕ → ℝ) := ∀ n m, n ≤ m → u n ≤ u m 119 | 120 | -- 0067 121 | example (u : ℕ → ℝ) (l : ℝ) (h : seq_limit u l) (h' : nondecreasing_seq u) : 122 | ∀ n, u n ≤ l := 123 | begin 124 | -- sorry 125 | intro n, 126 | by_contradiction H, 127 | push_neg at H, 128 | cases h ((u n - l)/2) (by linarith) with N hN, 129 | specialize hN (max n N) (le_max_right _ _), 130 | specialize h' n (max n N) (le_max_left _ _), 131 | rw abs_le at hN, 132 | linarith, 133 | -- sorry 134 | end 135 | 136 | /- 137 | In the following exercises, `A : set ℝ` means that A is a set of real numbers. 138 | We can use the usual notation x ∈ A. 139 | 140 | The notation `∀ x ∈ A, ...` is the abbreviation of `∀ x, x ∈ A → ... ` 141 | 142 | The notation `∃ x ∈ A, ...` is the abbreviation of `∃ x, x ∈ A ∧ ... `. 143 | More precisely it is the abbreviation of `∃ x (H : x ∈ A), ...` 144 | which is Lean's strange way of saying `∃ x, x ∈ A ∧ ... `. 145 | You can convert between these forms using the lemma 146 | exists_prop {p q : Prop} : (∃ (h : p), q) ↔ p ∧ q 147 | 148 | We'll work with upper bounds and supremums. 149 | Again we'll introduce specialized definitions for the sake of exercises, but mathlib 150 | has more general versions. 151 | 152 | 153 | def upper_bound (A : set ℝ) (x : ℝ) := ∀ a ∈ A, a ≤ x 154 | 155 | def is_sup (A : set ℝ) (x : ℝ) := upper_bound A x ∧ ∀ y, upper_bound A y → x ≤ y 156 | 157 | 158 | Remark: one can easily show that a set of real numbers has at most one sup, 159 | but we won't need this. 160 | -/ 161 | 162 | -- 0068 163 | example {A : set ℝ} {x : ℝ} (hx : is_sup A x) : 164 | ∀ y, y < x → ∃ a ∈ A, y < a := 165 | begin 166 | -- sorry 167 | intro y, 168 | contrapose!, 169 | exact hx.right y, 170 | -- sorry 171 | end 172 | 173 | /- 174 | Let's do a variation on an example from file 07 that will be useful in the last 175 | exercise below. 176 | -/ 177 | 178 | -- 0069 179 | lemma le_of_le_add_all' {x y : ℝ} : 180 | (∀ ε > 0, y ≤ x + ε) → y ≤ x := 181 | begin 182 | -- sorry 183 | contrapose!, 184 | intro h, 185 | use (y-x)/2, 186 | split ; linarith, 187 | -- sorry 188 | end 189 | 190 | -- 0070 191 | example {x y : ℝ} {u : ℕ → ℝ} (hu : seq_limit u x) 192 | (ineg : ∀ n, u n ≤ y) : x ≤ y := 193 | begin 194 | -- sorry 195 | apply le_of_le_add_all', 196 | intros ε ε_pos, 197 | cases hu ε ε_pos with N hN, 198 | specialize hN N (by linarith), 199 | rw abs_le at hN, 200 | linarith [ineg N], 201 | -- sorry 202 | end 203 | -------------------------------------------------------------------------------- /src/solutions/09_limits_final.lean: -------------------------------------------------------------------------------- 1 | import tuto_lib 2 | 3 | set_option pp.beta true 4 | set_option pp.coercions false 5 | 6 | /- 7 | This is the final file in the series. Here we use everything covered 8 | in previous files to prove a couple of famous theorems from 9 | elementary real analysis. Of course they all have more general versions 10 | in mathlib. 11 | 12 | As usual, keep in mind the following: 13 | 14 | abs_le {x y : ℝ} : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y 15 | 16 | ge_max_iff (p q r) : r ≥ max p q ↔ r ≥ p ∧ r ≥ q 17 | 18 | le_max_left p q : p ≤ max p q 19 | 20 | le_max_right p q : q ≤ max p q 21 | 22 | as well as a lemma from the previous file: 23 | 24 | le_of_le_add_all : (∀ ε > 0, y ≤ x + ε) → y ≤ x 25 | 26 | Let's start with a variation on a known exercise. 27 | -/ 28 | 29 | -- 0071 30 | lemma le_lim {x y : ℝ} {u : ℕ → ℝ} (hu : seq_limit u x) 31 | (ineg : ∃ N, ∀ n ≥ N, y ≤ u n) : y ≤ x := 32 | begin 33 | -- sorry 34 | apply le_of_le_add_all, 35 | intros ε ε_pos, 36 | cases hu ε ε_pos with N hN, 37 | cases ineg with N' hN', 38 | let N₀ := max N N', 39 | specialize hN N₀ (le_max_left N N'), 40 | specialize hN' N₀ (le_max_right N N'), 41 | rw abs_le at hN, 42 | linarith, 43 | -- sorry 44 | end 45 | 46 | /- 47 | Let's now return to the result proved in the `00_` file of this series, 48 | and prove again the sequential characterization of upper bounds (with a slighly 49 | different proof). 50 | 51 | For this, and other exercises below, we'll need many things that we proved in previous files, 52 | and a couple of extras. 53 | 54 | From the 5th file: 55 | 56 | limit_const (x : ℝ) : seq_limit (λ n, x) x 57 | 58 | 59 | squeeze (lim_u : seq_limit u l) (lim_w : seq_limit w l) 60 | (hu : ∀ n, u n ≤ v n) (hw : ∀ n, v n ≤ w n) : seq_limit v l 61 | 62 | From the 8th: 63 | 64 | def upper_bound (A : set ℝ) (x : ℝ) := ∀ a ∈ A, a ≤ x 65 | 66 | def is_sup (A : set ℝ) (x : ℝ) := upper_bound A x ∧ ∀ y, upper_bound A y → x ≤ y 67 | 68 | lt_sup (hx : is_sup A x) : ∀ y, y < x → ∃ a ∈ A, y < a := 69 | 70 | You can also use: 71 | 72 | nat.one_div_pos_of_nat {n : ℕ} : 0 < 1 / (n + 1 : ℝ) 73 | 74 | inv_succ_le_all : ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, 1/(n + 1 : ℝ) ≤ ε 75 | 76 | and their easy consequences: 77 | 78 | limit_of_sub_le_inv_succ (h : ∀ n, |u n - x| ≤ 1/(n+1)) : seq_limit u x 79 | 80 | limit_const_add_inv_succ (x : ℝ) : seq_limit (λ n, x + 1/(n+1)) x 81 | 82 | limit_const_sub_inv_succ (x : ℝ) : seq_limit (λ n, x - 1/(n+1)) x 83 | 84 | as well as: 85 | 86 | lim_le (hu : seq_limit u x) (ineg : ∀ n, u n ≤ y) : x ≤ y 87 | 88 | The structure of the proof is offered. It features a new tactic: 89 | `choose` which invokes the axiom of choice (observing the tactic state before and 90 | after using it should be enough to understand everything). 91 | -/ 92 | 93 | -- 0072 94 | lemma is_sup_iff (A : set ℝ) (x : ℝ) : 95 | (is_sup A x) ↔ (upper_bound A x ∧ ∃ u : ℕ → ℝ, seq_limit u x ∧ ∀ n, u n ∈ A ) := 96 | begin 97 | split, 98 | { intro h, 99 | split, 100 | { 101 | -- sorry 102 | exact h.left, 103 | -- sorry 104 | }, 105 | { have : ∀ n : ℕ, ∃ a ∈ A, x - 1/(n+1) < a, 106 | { intros n, 107 | have : 1/(n+1 : ℝ) > 0, 108 | exact nat.one_div_pos_of_nat, 109 | -- sorry 110 | exact lt_sup h _ (by linarith), 111 | -- sorry 112 | }, 113 | choose u hu using this, 114 | -- sorry 115 | use u, 116 | split, 117 | { apply squeeze (limit_const_sub_inv_succ x) (limit_const x), 118 | { intros n, 119 | exact le_of_lt (hu n).2, }, 120 | { intro n, 121 | exact h.1 _ (hu n).left, } }, 122 | { intro n, 123 | exact (hu n).left }, 124 | -- sorry 125 | } }, 126 | { rintro ⟨maj, u, limu, u_in⟩, 127 | -- sorry 128 | split, 129 | { exact maj }, 130 | { intros y ymaj, 131 | apply lim_le limu, 132 | intro n, 133 | apply ymaj, 134 | apply u_in }, 135 | -- sorry 136 | }, 137 | end 138 | 139 | /-- Continuity of a function at a point -/ 140 | def continuous_at_pt (f : ℝ → ℝ) (x₀ : ℝ) : Prop := 141 | ∀ ε > 0, ∃ δ > 0, ∀ x, |x - x₀| ≤ δ → |f x - f x₀| ≤ ε 142 | 143 | variables {f : ℝ → ℝ} {x₀ : ℝ} {u : ℕ → ℝ} 144 | 145 | -- 0073 146 | lemma seq_continuous_of_continuous (hf : continuous_at_pt f x₀) 147 | (hu : seq_limit u x₀) : seq_limit (f ∘ u) (f x₀) := 148 | begin 149 | -- sorry 150 | intros ε ε_pos, 151 | rcases hf ε ε_pos with ⟨δ, δ_pos, hδ⟩, 152 | cases hu δ δ_pos with N hN, 153 | use N, 154 | intros n hn, 155 | apply hδ, 156 | exact hN n hn, 157 | -- sorry 158 | end 159 | 160 | -- 0074 161 | example : 162 | (∀ u : ℕ → ℝ, seq_limit u x₀ → seq_limit (f ∘ u) (f x₀)) → 163 | continuous_at_pt f x₀ := 164 | begin 165 | -- sorry 166 | contrapose!, 167 | intro hf, 168 | unfold continuous_at_pt at hf, 169 | push_neg at hf, 170 | cases hf with ε h, 171 | cases h with ε_pos hf, 172 | have H : ∀ n : ℕ, ∃ x, |x - x₀| ≤ 1/(n+1) ∧ ε < |f x - f x₀|, 173 | intro n, 174 | apply hf, 175 | exact nat.one_div_pos_of_nat, 176 | clear hf, 177 | choose u hu using H, 178 | use u, 179 | split, 180 | intros η η_pos, 181 | have fait : ∃ (N : ℕ), ∀ (n : ℕ), n ≥ N → 1 / (↑n + 1) ≤ η, 182 | exact inv_succ_le_all η η_pos, 183 | cases fait with N hN, 184 | use N, 185 | intros n hn, 186 | calc |u n - x₀| ≤ 1/(n+1) : (hu n).left 187 | ... ≤ η : hN n hn, 188 | unfold seq_limit, 189 | push_neg, 190 | use [ε, ε_pos], 191 | intro N, 192 | use N, 193 | split, 194 | linarith, 195 | exact (hu N).right, 196 | -- sorry 197 | end 198 | 199 | /- 200 | Recall from the 6th file: 201 | 202 | 203 | def extraction (φ : ℕ → ℕ) := ∀ n m, n < m → φ n < φ m 204 | 205 | def cluster_point (u : ℕ → ℝ) (a : ℝ) := 206 | ∃ φ, extraction φ ∧ seq_limit (u ∘ φ) a 207 | 208 | 209 | id_le_extraction : extraction φ → ∀ n, n ≤ φ n 210 | 211 | and from the 8th file: 212 | 213 | def tendsto_infinity (u : ℕ → ℝ) := ∀ A, ∃ N, ∀ n ≥ N, u n ≥ A 214 | 215 | not_seq_limit_of_tendstoinfinity : tendsto_infinity u → ∀ l, ¬ seq_limit u l 216 | -/ 217 | 218 | variables {φ : ℕ → ℕ} 219 | 220 | 221 | -- 0075 222 | lemma subseq_tendstoinfinity 223 | (h : tendsto_infinity u) (hφ : extraction φ) : 224 | tendsto_infinity (u ∘ φ) := 225 | begin 226 | -- sorry 227 | intros A, 228 | cases h A with N hN, 229 | use N, 230 | intros n hn, 231 | apply hN, 232 | calc N ≤ n : hn 233 | ... ≤ φ n : id_le_extraction hφ n, 234 | -- sorry 235 | end 236 | 237 | -- 0076 238 | lemma squeeze_infinity {u v : ℕ → ℝ} (hu : tendsto_infinity u) 239 | (huv : ∀ n, u n ≤ v n) : tendsto_infinity v := 240 | begin 241 | -- sorry 242 | intros A, 243 | cases hu A with N hN, 244 | use N, 245 | intros n hn, 246 | specialize hN n hn, 247 | specialize huv n, 248 | linarith, 249 | -- sorry 250 | end 251 | 252 | /- 253 | We will use segments: Icc a b := { x | a ≤ x ∧ x ≤ b } 254 | The notation stands for Interval-closed-closed. Variations exist with 255 | o or i instead of c, where o stands for open and i for infinity. 256 | 257 | We will use the following version of Bolzano-Weierstrass 258 | 259 | bolzano_weierstrass (h : ∀ n, u n ∈ [a, b]) : 260 | ∃ c ∈ [a, b], cluster_point u c 261 | 262 | as well as the obvious 263 | 264 | seq_limit_id : tendsto_infinity (λ n, n) 265 | -/ 266 | open set 267 | 268 | 269 | -- 0077 270 | lemma bdd_above_segment {f : ℝ → ℝ} {a b : ℝ} (hf : ∀ x ∈ Icc a b, continuous_at_pt f x) : 271 | ∃ M, ∀ x ∈ Icc a b, f x ≤ M := 272 | begin 273 | -- sorry 274 | by_contradiction H, 275 | push_neg at H, 276 | have clef : ∀ n : ℕ, ∃ x, x ∈ Icc a b ∧ f x > n, 277 | intro n, apply H, clear H, 278 | choose u hu using clef, 279 | have lim_infinie : tendsto_infinity (f ∘ u), 280 | apply squeeze_infinity (seq_limit_id), 281 | intros n, 282 | specialize hu n, 283 | linarith, 284 | have bornes : ∀ n, u n ∈ Icc a b, 285 | intro n, exact (hu n).left, 286 | rcases bolzano_weierstrass bornes with ⟨c, c_dans, φ, φ_extr, lim⟩, 287 | have lim_infinie_extr : tendsto_infinity (f ∘ (u ∘ φ)), 288 | exact subseq_tendstoinfinity lim_infinie φ_extr, 289 | have lim_extr : seq_limit (f ∘ (u ∘ φ)) (f c), 290 | exact seq_continuous_of_continuous (hf c c_dans) lim, 291 | exact not_seq_limit_of_tendstoinfinity lim_infinie_extr (f c) lim_extr, 292 | -- sorry 293 | end 294 | 295 | /- 296 | In the next exercise, we can use: 297 | 298 | abs_neg x : |-x| = |x| 299 | -/ 300 | 301 | -- 0078 302 | lemma continuous_opposite {f : ℝ → ℝ} {x₀ : ℝ} (h : continuous_at_pt f x₀) : 303 | continuous_at_pt (λ x, -f x) x₀ := 304 | begin 305 | -- sorry 306 | intros ε ε_pos, 307 | cases h ε ε_pos with δ h, 308 | cases h with δ_pos h, 309 | use [δ, δ_pos], 310 | intros y hy, 311 | have : -f y - -f x₀ = -(f y - f x₀), ring, 312 | rw [this, abs_neg], 313 | exact h y hy, 314 | -- sorry 315 | end 316 | 317 | /- 318 | Now let's combine the two exercises above 319 | -/ 320 | 321 | -- 0079 322 | lemma bdd_below_segment {f : ℝ → ℝ} {a b : ℝ} (hf : ∀ x ∈ Icc a b, continuous_at_pt f x) : 323 | ∃ m, ∀ x ∈ Icc a b, m ≤ f x := 324 | begin 325 | -- sorry 326 | have : ∃ M, ∀ x ∈ Icc a b, -f x ≤ M, 327 | { apply bdd_above_segment, 328 | intros x x_dans, 329 | exact continuous_opposite (hf x x_dans), }, 330 | cases this with M hM, 331 | use -M, 332 | intros x x_dans, 333 | specialize hM x x_dans, 334 | linarith, 335 | -- sorry 336 | end 337 | 338 | /- 339 | Remember from the 5th file: 340 | 341 | unique_limit : seq_limit u l → seq_limit u l' → l = l' 342 | 343 | and from the 6th one: 344 | 345 | subseq_tendsto_of_tendsto (h : seq_limit u l) (hφ : extraction φ) : 346 | seq_limit (u ∘ φ) l 347 | 348 | We now admit the following version of the least upper bound theorem 349 | (that cannot be proved without discussing the construction of real numbers 350 | or admitting another strong theorem). 351 | 352 | sup_segment {a b : ℝ} {A : set ℝ} (hnonvide : ∃ x, x ∈ A) (h : A ⊆ Icc a b) : 353 | ∃ x ∈ Icc a b, is_sup A x 354 | 355 | In the next exercise, it can be useful to prove inclusions of sets of real number. 356 | By definition, A ⊆ B means : ∀ x, x ∈ A → x ∈ B. 357 | Hence one can start a proof of A ⊆ B by `intros x x_in`, 358 | which brings `x : ℝ` and `x_in : x ∈ A` in the local context, 359 | and then prove `x ∈ B`. 360 | 361 | Note also the use of 362 | {x | P x} 363 | which denotes the set of x satisfying predicate P. 364 | 365 | Hence `x' ∈ { x | P x} ↔ P x'`, by definition. 366 | -/ 367 | 368 | -- 0080 369 | example {a b : ℝ} (hab : a ≤ b) (hf : ∀ x ∈ Icc a b, continuous_at_pt f x) : 370 | ∃ x₀ ∈ Icc a b, ∀ x ∈ Icc a b, f x ≤ f x₀ := 371 | begin 372 | -- sorry 373 | cases bdd_below_segment hf with m hm, 374 | cases bdd_above_segment hf with M hM, 375 | let A := {y | ∃ x ∈ Icc a b, y = f x}, 376 | obtain ⟨y₀, y_dans, y_sup⟩ : ∃ y₀ ∈ Icc m M, is_sup A y₀, 377 | { apply sup_segment, 378 | { use [f a, a, by linarith, hab, by ring], }, 379 | { rintros y ⟨x, x_in, rfl⟩, 380 | exact ⟨hm x x_in, hM x x_in⟩ } }, 381 | rw is_sup_iff at y_sup, 382 | rcases y_sup with ⟨y_maj, u, lim_u, u_dans⟩, 383 | choose v hv using u_dans, 384 | cases forall_and_distrib.mp hv with v_dans hufv, 385 | replace hufv : u = f ∘ v := funext hufv, 386 | rcases bolzano_weierstrass v_dans with ⟨x₀, x₀_in, φ, φ_extr, lim_vφ⟩, 387 | use [x₀, x₀_in], 388 | intros x x_dans, 389 | have lim : seq_limit (f ∘ v ∘ φ) (f x₀), 390 | { apply seq_continuous_of_continuous, 391 | exact hf x₀ x₀_in, 392 | exact lim_vφ }, 393 | have unique : f x₀ = y₀, 394 | { apply unique_limit lim, 395 | rw hufv at lim_u, 396 | exact subseq_tendsto_of_tendsto lim_u φ_extr }, 397 | rw unique, 398 | apply y_maj, 399 | use [x, x_dans], 400 | -- sorry 401 | end 402 | 403 | lemma stupid {a b x : ℝ} (h : x ∈ Icc a b) (h' : x ≠ b) : x < b := 404 | lt_of_le_of_ne h.right h' 405 | 406 | /- 407 | And now the final boss... 408 | -/ 409 | 410 | def I := (Icc 0 1 : set ℝ) -- the type ascription makes sure 0 and 1 are real numbers here 411 | 412 | -- 0081 413 | example (f : ℝ → ℝ) (hf : ∀ x, continuous_at_pt f x) (h₀ : f 0 < 0) (h₁ : f 1 > 0) : 414 | ∃ x₀ ∈ I, f x₀ = 0 := 415 | begin 416 | let A := { x | x ∈ I ∧ f x < 0}, 417 | have ex_x₀ : ∃ x₀ ∈ I, is_sup A x₀, 418 | { 419 | -- sorry 420 | apply sup_segment, 421 | use 0, 422 | split, 423 | split, linarith, linarith, 424 | exact h₀, 425 | intros x hx, 426 | exact hx.left 427 | -- sorry 428 | }, 429 | rcases ex_x₀ with ⟨x₀, x₀_in, x₀_sup⟩, 430 | use [x₀, x₀_in], 431 | have : f x₀ ≤ 0, 432 | { 433 | -- sorry 434 | rw is_sup_iff at x₀_sup, 435 | rcases x₀_sup with ⟨maj_x₀, u, lim_u, u_dans⟩, 436 | have : seq_limit (f ∘ u) (f x₀), 437 | exact seq_continuous_of_continuous (hf x₀) lim_u, 438 | apply lim_le this, 439 | intros n, 440 | have : f (u n) < 0, 441 | exact (u_dans n).right, 442 | linarith 443 | -- sorry 444 | }, 445 | have x₀_1: x₀ < 1, 446 | { 447 | -- sorry 448 | apply stupid x₀_in, 449 | intro h, 450 | rw ← h at h₁, 451 | linarith 452 | -- sorry 453 | }, 454 | have : f x₀ ≥ 0, 455 | { have in_I : ∃ N : ℕ, ∀ n ≥ N, x₀ + 1/(n+1) ∈ I, 456 | { have : ∃ N : ℕ, ∀ n≥ N, 1/(n+1 : ℝ) ≤ 1-x₀, 457 | { 458 | -- sorry 459 | apply inv_succ_le_all, 460 | linarith, 461 | -- sorry 462 | }, 463 | -- sorry 464 | cases this with N hN, 465 | use N, 466 | intros n hn, 467 | specialize hN n hn, 468 | have : 1/(n+1 : ℝ) > 0, 469 | exact nat.one_div_pos_of_nat, 470 | change 0 ≤ x₀ ∧ x₀ ≤ 1 at x₀_in, 471 | split ; linarith, 472 | -- sorry 473 | }, 474 | have not_in : ∀ n : ℕ, x₀ + 1/(n+1) ∉ A, 475 | -- By definition, x ∉ A means ¬ (x ∈ A). 476 | { 477 | -- sorry 478 | intros n hn, 479 | cases x₀_sup with x₀_maj _, 480 | specialize x₀_maj _ hn, 481 | have : 1/(n+1 : ℝ) > 0, 482 | from nat.one_div_pos_of_nat, 483 | linarith, 484 | -- sorry 485 | }, 486 | dsimp [A] at not_in, 487 | -- sorry 488 | push_neg at not_in, 489 | have lim : seq_limit (λ n, f(x₀ + 1/(n+1))) (f x₀), 490 | { apply seq_continuous_of_continuous (hf x₀), 491 | apply limit_const_add_inv_succ }, 492 | apply le_lim lim, 493 | cases in_I with N hN, 494 | use N, 495 | intros n hn, 496 | exact not_in n (hN n hn), 497 | -- sorry 498 | }, 499 | linarith, 500 | end 501 | -------------------------------------------------------------------------------- /src/solutions/tuto_lib.lean: -------------------------------------------------------------------------------- 1 | import analysis.specific_limits.basic 2 | import data.int.parity 3 | import topology.sequences 4 | 5 | attribute [instance] classical.prop_decidable 6 | 7 | /- 8 | Lemmas from that file were hidden in my course, or restating things which 9 | were proved without name in previous files. 10 | -/ 11 | 12 | notation `|`x`|` := abs x 13 | 14 | -- The mathlib version is unusable because it is stated in terms of ≤ 15 | lemma ge_max_iff {α : Type*} [linear_order α] {p q r : α} : r ≥ max p q ↔ r ≥ p ∧ r ≥ q := 16 | max_le_iff 17 | 18 | /- No idea why this is not in mathlib-/ 19 | lemma eq_of_abs_sub_le_all (x y : ℝ) : (∀ ε > 0, |x - y| ≤ ε) → x = y := 20 | begin 21 | intro h, 22 | apply eq_of_abs_sub_nonpos, 23 | by_contradiction H, 24 | push_neg at H, 25 | specialize h ( |x-y|/2) (by linarith), 26 | linarith, 27 | end 28 | 29 | def seq_limit (u : ℕ → ℝ) (l : ℝ) : Prop := 30 | ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε 31 | 32 | lemma unique_limit {u l l'} : seq_limit u l → seq_limit u l' → l = l' := 33 | begin 34 | intros hl hl', 35 | apply eq_of_abs_sub_le_all, 36 | intros ε ε_pos, 37 | specialize hl (ε/2) (by linarith), 38 | cases hl with N hN, 39 | specialize hl' (ε/2) (by linarith), 40 | cases hl' with N' hN', 41 | specialize hN (max N N') (le_max_left _ _), 42 | specialize hN' (max N N') (le_max_right _ _), 43 | calc |l - l'| = |(l-u (max N N')) + (u (max N N') -l')| : by ring_nf 44 | ... ≤ |l - u (max N N')| + |u (max N N') - l'| : by apply abs_add 45 | ... = |u (max N N') - l| + |u (max N N') - l'| : by rw abs_sub_comm 46 | ... ≤ ε/2 + ε/2 : by linarith 47 | ... = ε : by ring, 48 | end 49 | 50 | lemma le_of_le_add_all {x y : ℝ} : 51 | (∀ ε > 0, y ≤ x + ε) → y ≤ x := 52 | begin 53 | contrapose!, 54 | intro h, 55 | use (y-x)/2, 56 | split ; linarith, 57 | end 58 | 59 | def upper_bound (A : set ℝ) (x : ℝ) := ∀ a ∈ A, a ≤ x 60 | 61 | def is_sup (A : set ℝ) (x : ℝ) := upper_bound A x ∧ ∀ y, upper_bound A y → x ≤ y 62 | 63 | lemma lt_sup {A : set ℝ} {x : ℝ} (hx : is_sup A x) : 64 | ∀ y, y < x → ∃ a ∈ A, y < a := 65 | begin 66 | intro y, 67 | contrapose!, 68 | exact hx.right y, 69 | end 70 | 71 | lemma squeeze {u v w : ℕ → ℝ} {l} (hu : seq_limit u l) (hw : seq_limit w l) 72 | (h : ∀ n, u n ≤ v n) 73 | (h' : ∀ n, v n ≤ w n) : seq_limit v l := 74 | begin 75 | intros ε ε_pos, 76 | cases hu ε ε_pos with N hN, 77 | cases hw ε ε_pos with N' hN', 78 | use max N N', 79 | intros n hn, 80 | rw ge_max_iff at hn, 81 | specialize hN n (by linarith), 82 | specialize hN' n (by linarith), 83 | specialize h n, 84 | specialize h' n, 85 | rw abs_le at *, 86 | split ; linarith 87 | end 88 | 89 | def extraction (φ : ℕ → ℕ) := ∀ n m, n < m → φ n < φ m 90 | 91 | def tendsto_infinity (u : ℕ → ℝ) := ∀ A, ∃ N, ∀ n ≥ N, u n ≥ A 92 | 93 | lemma lim_le {x y : ℝ} {u : ℕ → ℝ} (hu : seq_limit u x) 94 | (ineg : ∀ n, u n ≤ y) : x ≤ y := 95 | begin 96 | apply le_of_le_add_all, 97 | intros ε ε_pos, 98 | cases hu ε ε_pos with N hN, 99 | specialize hN N (by linarith), 100 | specialize ineg N, 101 | rw abs_le at hN, 102 | linarith, 103 | end 104 | 105 | lemma inv_succ_le_all : ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, 1/(n + 1 : ℝ) ≤ ε := 106 | begin 107 | convert metric.tendsto_at_top.mp (tendsto_one_div_add_at_top_nhds_0_nat), 108 | apply propext, 109 | simp only [real.dist_eq, sub_zero], 110 | split, 111 | intros h ε ε_pos, 112 | cases h (ε/2) (by linarith) with N hN, 113 | use N, 114 | intros n hn, 115 | rw abs_of_pos (nat.one_div_pos_of_nat : 1/(n+1 : ℝ) > 0), 116 | specialize hN n hn, 117 | linarith, 118 | intros h ε ε_pos, 119 | cases h ε (by linarith) with N hN, 120 | use N, 121 | intros n hn, 122 | specialize hN n hn, 123 | rw abs_of_pos (@nat.one_div_pos_of_nat ℝ _ n) at hN, 124 | linarith, 125 | end 126 | 127 | lemma limit_const (x : ℝ) : seq_limit (λ n, x) x := 128 | λ ε ε_pos, ⟨0, λ _ _, by simp [le_of_lt ε_pos]⟩ 129 | 130 | lemma limit_of_sub_le_inv_succ {u : ℕ → ℝ} {x : ℝ} (h : ∀ n, |u n - x| ≤ 1/(n+1)) : 131 | seq_limit u x := 132 | begin 133 | intros ε ε_pos, 134 | rcases inv_succ_le_all ε ε_pos with ⟨N, hN⟩, 135 | use N, 136 | intros n hn, 137 | specialize h n, 138 | specialize hN n hn, 139 | linarith, 140 | end 141 | 142 | lemma limit_const_add_inv_succ (x : ℝ) : seq_limit (λ n, x + 1/(n+1)) x := 143 | limit_of_sub_le_inv_succ (λ n, by rw abs_of_pos ; linarith [@nat.one_div_pos_of_nat ℝ _ n]) 144 | 145 | lemma limit_const_sub_inv_succ (x : ℝ) : seq_limit (λ n, x - 1/(n+1)) x := 146 | begin 147 | refine limit_of_sub_le_inv_succ (λ n, _), 148 | rw [show x - 1 / (n + 1) - x = -(1/(n+1)), by ring, abs_neg, abs_of_pos], 149 | linarith [@nat.one_div_pos_of_nat ℝ _ n] 150 | end 151 | 152 | lemma id_le_extraction {φ}: extraction φ → ∀ n, n ≤ φ n := 153 | begin 154 | intros hyp n, 155 | induction n with n hn, 156 | { exact nat.zero_le _ }, 157 | { exact nat.succ_le_of_lt (by linarith [hyp n (n+1) (by linarith)]) }, 158 | end 159 | 160 | lemma seq_limit_id : tendsto_infinity (λ n, n) := 161 | begin 162 | intros A, 163 | cases exists_nat_gt A with N hN, 164 | use N, 165 | intros n hn, 166 | have : (n : ℝ) ≥ N, exact_mod_cast hn, 167 | linarith, 168 | end 169 | 170 | variables {u : ℕ → ℝ} {l : ℝ} {φ : ℕ → ℕ} 171 | 172 | open set filter 173 | 174 | def cluster_point (u : ℕ → ℝ) (a : ℝ) := 175 | ∃ φ, extraction φ ∧ seq_limit (u ∘ φ) a 176 | 177 | lemma bolzano_weierstrass {a b : ℝ} {u : ℕ → ℝ} (h : ∀ n, u n ∈ Icc a b) : 178 | ∃ c ∈ Icc a b, cluster_point u c := 179 | begin 180 | rcases (is_compact_Icc : is_compact (Icc a b)).tendsto_subseq h with ⟨c, c_in, φ, hφ, lim⟩, 181 | use [c, c_in, φ, hφ], 182 | simp_rw [metric.tendsto_nhds, eventually_at_top, real.dist_eq] at lim, 183 | intros ε ε_pos, 184 | rcases lim ε ε_pos with ⟨N, hN⟩, 185 | use N, 186 | intros n hn, 187 | exact le_of_lt (hN n hn) 188 | end 189 | 190 | lemma not_seq_limit_of_tendstoinfinity {u : ℕ → ℝ} : 191 | tendsto_infinity u → ∀ x, ¬ seq_limit u x := 192 | begin 193 | intros lim_infinie x lim_x, 194 | cases lim_x 1 (by linarith) with N hN, 195 | cases lim_infinie (x+2) with N' hN', 196 | let N₀ := max N N', 197 | specialize hN N₀ (le_max_left _ _), 198 | specialize hN' N₀ (le_max_right _ _), 199 | rw abs_le at hN, 200 | linarith, 201 | end 202 | 203 | open real 204 | 205 | lemma sup_segment {a b : ℝ} {A : set ℝ} (hnonvide : ∃ x, x ∈ A) (h : A ⊆ Icc a b) : 206 | ∃ x ∈ Icc a b, is_sup A x := 207 | begin 208 | have b_maj : ∀ (y : ℝ), y ∈ A → y ≤ b, 209 | from λ y y_in, (h y_in).2, 210 | have Sup_maj : upper_bound A (Sup A), 211 | { intro x, 212 | apply le_cSup, 213 | use [b, b_maj] } , 214 | refine ⟨Sup A, _, _⟩, 215 | { split, 216 | { cases hnonvide with x x_in, 217 | exact le_trans (h x_in).1 (Sup_maj _ x_in) }, 218 | { apply cSup_le hnonvide b_maj } }, 219 | { exact ⟨Sup_maj, λ y, cSup_le hnonvide⟩ }, 220 | end 221 | 222 | lemma subseq_tendsto_of_tendsto (h : seq_limit u l) (hφ : extraction φ) : 223 | seq_limit (u ∘ φ) l := 224 | begin 225 | intros ε ε_pos, 226 | cases h ε ε_pos with N hN, 227 | use N, 228 | intros n hn, 229 | apply hN, 230 | calc N ≤ n : hn 231 | ... ≤ φ n : id_le_extraction hφ n, 232 | end 233 | namespace tactic.interactive 234 | open tactic 235 | 236 | meta def check_me : tactic unit := 237 | `[ { repeat { unfold seq_limit}, 238 | repeat { unfold continue_en }, 239 | push_neg, 240 | try { simp only [exists_prop] }, 241 | try { exact iff.rfl }, 242 | done } <|> fail "That's not quite right. Please try again." ] 243 | 244 | end tactic.interactive 245 | --------------------------------------------------------------------------------