diff --git a/.github/build.in.yml b/.github/build.in.yml index e2686a75018e68..c3248f810809a4 100644 --- a/.github/build.in.yml +++ b/.github/build.in.yml @@ -136,9 +136,8 @@ jobs: shell: bash # We're only building the `master` branch version of the tools, so don't need to run inside landrun. run: | cd master-branch - lake build cache mk_all check-yaml graph + lake build cache check-yaml graph ls .lake/build/bin/cache - ls .lake/build/bin/mk_all ls .lake/build/bin/check-yaml ls .lake/packages/importGraph/.lake/build/bin/graph @@ -219,13 +218,11 @@ jobs: - name: update {Mathlib, Tactic, Counterexamples, Archive}.lean id: mk_all continue-on-error: true # Allow workflow to continue, outcome checked later - # This only runs `mk_all --check` from the `master-branch`, so doesn't need to be inside landrun - shell: bash + # This runs `mk_all --check` from the `pr-branch` inside landrun run: | cd pr-branch - - echo "Running mk_all --check (from master-branch)..." - LAKE_HOME="$TOOLCHAIN_DIR" ../master-branch/.lake/build/bin/mk_all --check + echo "Running mk_all --check (from pr-branch)..." + lake exe mk_all --check - name: begin gh-problem-match-wrap for build step uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 @@ -422,6 +419,7 @@ jobs: post_steps: name: Post-Build StepJOB_NAME + if: FORK_CONDITION needs: [build] runs-on: ubuntu-latest # Note these steps run on disposable GitHub runners, so no landrun sandboxing is needed. steps: @@ -503,6 +501,7 @@ jobs: style_lint: name: Lint styleJOB_NAME + if: FORK_CONDITION runs-on: ubuntu-latest steps: - uses: leanprover-community/lint-style-action@f2e7272aad56233a642b08fe974cf09dd664b0c8 # 2025-05-22 @@ -510,13 +509,26 @@ jobs: mode: check lint-bib-file: true + build_and_lint: + name: CI Success + if: FORK_CONDITION + needs: [style_lint, post_steps] + runs-on: ubuntu-latest + steps: + - name: succeed + run: | + echo "Success: Required build and lint checks completed!" + final: name: Post-CI jobJOB_NAME if: FORK_CONDITION needs: [style_lint, build, post_steps] runs-on: ubuntu-latest steps: - - id: PR + # This action is used to determine the PR metadata in the event of a push. + # If it is called from a PR from a fork, it will find nothing/irrelevant data. + - if: github.event_name != 'pull_request_target' + id: PR_from_push uses: 8BitJonny/gh-get-current-pr@08e737c57a3a4eb24cec6487664b243b77eb5e36 # 3.0.0 # TODO: this may not work properly if the same commit is pushed to multiple branches: # https://github.com/8BitJonny/gh-get-current-pr/issues/8 @@ -525,6 +537,13 @@ jobs: # Only return if PR is still open filterOutClosed: true + # Combine the output from the previous action with the metadata supplied by GitHub itself. + - id: PR + shell: bash + run: | + echo "number=${{ steps.PR_from_push.outputs.number || github.event.pull_request.number }}" >> $GITHUB_OUTPUT + echo "pr_labels=${{ steps.PR_from_push.outputs.pr_labels || join(github.event.pull_request.labels.*.name, ',') }}" >> $GITHUB_OUTPUT + - if: contains(steps.PR.outputs.pr_labels, 'bench-after-CI') name: If `bench-after-CI` is present, add a `!bench` comment. uses: GrantBirki/comment@608e41b19bc973020ec0e189ebfdae935d7fe0cc # v2.1.1 diff --git a/.github/workflows/PR_summary.yml b/.github/workflows/PR_summary.yml index 864dff284745af..788f9696b4f02d 100644 --- a/.github/workflows/PR_summary.yml +++ b/.github/workflows/PR_summary.yml @@ -3,6 +3,12 @@ name: Post PR summary comment on: pull_request_target: +# Limit permissions for GITHUB_TOKEN for the entire workflow +permissions: + contents: read + pull-requests: write # Only allow PR comments/labels + # All other permissions are implicitly 'none' + jobs: build: name: "post-or-update-summary-comment" diff --git a/.github/workflows/add_label_from_diff.yaml b/.github/workflows/add_label_from_diff.yaml index a86bd7601b7e5e..4bb896b1578bdd 100644 --- a/.github/workflows/add_label_from_diff.yaml +++ b/.github/workflows/add_label_from_diff.yaml @@ -1,42 +1,64 @@ name: Autolabel PRs on: - pull_request: + pull_request_target: types: [opened] push: paths: - scripts/autolabel.lean - .github/workflows/add_label_from_diff.yaml +# Limit permissions for GITHUB_TOKEN for the entire workflow +permissions: + contents: read + pull-requests: write # Only allow PR comments/labels + # All other permissions are implicitly 'none' + jobs: add_topic_label: name: Add topic label runs-on: ubuntu-latest # Don't run on forks, where we wouldn't have permissions to add the label anyway. if: github.repository == 'leanprover-community/mathlib4' - permissions: - issues: write - checks: write - pull-requests: write - contents: read steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - name: Configure Lean - uses: leanprover/lean-action@f807b338d95de7813c5c50d018f1c23c9b93b4ec # 2025-04-24 - with: - auto-config: false - use-github-cache: false - use-mathlib-cache: false - - name: lake exe autolabel - run: | - # the checkout dance, to avoid a detached head - git checkout master - git checkout - - lake exe autolabel "$NUMBER" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GH_REPO: ${{ github.repository }} - NUMBER: ${{ github.event.number }} + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + - name: Configure Lean + uses: leanprover/lean-action@f807b338d95de7813c5c50d018f1c23c9b93b4ec # 2025-04-24 + with: + auto-config: false + use-github-cache: false + use-mathlib-cache: false + - name: lake exe autolabel + run: | + # the checkout dance, to avoid a detached head + git checkout master + git checkout - + labels="$(lake exe autolabel)" + printf '%s\n' "${labels}" + # extract + label="$(printf '%s' "${labels}" | sed -n 's=.*#\[\([^,]*\)\].*=\1=p')" + printf 'label: "%s"\n' "${label}" + if [ -n "${label}" ] + then + printf 'Applying label %s\n' "${label}" + # we use curl rather than octokit/request-action so that the job won't fail + # (and send an annoying email) if the labels don't exist + url="https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels" + printf 'url: %s\n' "${url}" + jsonLabel="$(printf '{"labels":["%s"]}' "${label}")" + printf 'jsonLabel: %s\n' "${jsonLabel}" + curl --request POST \ + --header 'Accept: application/vnd.github+json' \ + --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \ + --header 'X-GitHub-Api-Version: 2022-11-28' \ + --url "${url}" \ + --data "${jsonLabel}" + else + echo "There is no single label that we could apply, so we are not applying any label." + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/bors.yml b/.github/workflows/bors.yml index 5a796c2b6eb137..b519aa43c0dfc4 100644 --- a/.github/workflows/bors.yml +++ b/.github/workflows/bors.yml @@ -38,7 +38,7 @@ permissions: jobs: build: - if: true + if: github.repository == 'leanprover-community/mathlib4' name: Build runs-on: bors outputs: @@ -146,9 +146,8 @@ jobs: shell: bash # We're only building the `master` branch version of the tools, so don't need to run inside landrun. run: | cd master-branch - lake build cache mk_all check-yaml graph + lake build cache check-yaml graph ls .lake/build/bin/cache - ls .lake/build/bin/mk_all ls .lake/build/bin/check-yaml ls .lake/packages/importGraph/.lake/build/bin/graph @@ -229,13 +228,11 @@ jobs: - name: update {Mathlib, Tactic, Counterexamples, Archive}.lean id: mk_all continue-on-error: true # Allow workflow to continue, outcome checked later - # This only runs `mk_all --check` from the `master-branch`, so doesn't need to be inside landrun - shell: bash + # This runs `mk_all --check` from the `pr-branch` inside landrun run: | cd pr-branch - - echo "Running mk_all --check (from master-branch)..." - LAKE_HOME="$TOOLCHAIN_DIR" ../master-branch/.lake/build/bin/mk_all --check + echo "Running mk_all --check (from pr-branch)..." + lake exe mk_all --check - name: begin gh-problem-match-wrap for build step uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 @@ -432,6 +429,7 @@ jobs: post_steps: name: Post-Build Step + if: github.repository == 'leanprover-community/mathlib4' needs: [build] runs-on: ubuntu-latest # Note these steps run on disposable GitHub runners, so no landrun sandboxing is needed. steps: @@ -513,6 +511,7 @@ jobs: style_lint: name: Lint style + if: github.repository == 'leanprover-community/mathlib4' runs-on: ubuntu-latest steps: - uses: leanprover-community/lint-style-action@f2e7272aad56233a642b08fe974cf09dd664b0c8 # 2025-05-22 @@ -520,13 +519,26 @@ jobs: mode: check lint-bib-file: true + build_and_lint: + name: CI Success + if: github.repository == 'leanprover-community/mathlib4' + needs: [style_lint, post_steps] + runs-on: ubuntu-latest + steps: + - name: succeed + run: | + echo "Success: Required build and lint checks completed!" + final: name: Post-CI job - if: true + if: github.repository == 'leanprover-community/mathlib4' needs: [style_lint, build, post_steps] runs-on: ubuntu-latest steps: - - id: PR + # This action is used to determine the PR metadata in the event of a push. + # If it is called from a PR from a fork, it will find nothing/irrelevant data. + - if: github.event_name != 'pull_request_target' + id: PR_from_push uses: 8BitJonny/gh-get-current-pr@08e737c57a3a4eb24cec6487664b243b77eb5e36 # 3.0.0 # TODO: this may not work properly if the same commit is pushed to multiple branches: # https://github.com/8BitJonny/gh-get-current-pr/issues/8 @@ -535,6 +547,13 @@ jobs: # Only return if PR is still open filterOutClosed: true + # Combine the output from the previous action with the metadata supplied by GitHub itself. + - id: PR + shell: bash + run: | + echo "number=${{ steps.PR_from_push.outputs.number || github.event.pull_request.number }}" >> $GITHUB_OUTPUT + echo "pr_labels=${{ steps.PR_from_push.outputs.pr_labels || join(github.event.pull_request.labels.*.name, ',') }}" >> $GITHUB_OUTPUT + - if: contains(steps.PR.outputs.pr_labels, 'bench-after-CI') name: If `bench-after-CI` is present, add a `!bench` comment. uses: GrantBirki/comment@608e41b19bc973020ec0e189ebfdae935d7fe0cc # v2.1.1 diff --git a/.github/workflows/bot_fix_style.yaml b/.github/workflows/bot_fix_style.yaml index 586c8b9266fe2b..5081f2cd1b84eb 100644 --- a/.github/workflows/bot_fix_style.yaml +++ b/.github/workflows/bot_fix_style.yaml @@ -31,4 +31,4 @@ jobs: with: mode: fix lint-bib-file: true - bot-fix-style-token: ${{ secrets.GITHUB_TOKEN }} + BOT_FIX_STYLE_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 20c535801b21f5..698a29cffab08a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,7 +45,7 @@ permissions: jobs: build: - if: true + if: github.repository == 'leanprover-community/mathlib4' name: Build runs-on: pr outputs: @@ -153,9 +153,8 @@ jobs: shell: bash # We're only building the `master` branch version of the tools, so don't need to run inside landrun. run: | cd master-branch - lake build cache mk_all check-yaml graph + lake build cache check-yaml graph ls .lake/build/bin/cache - ls .lake/build/bin/mk_all ls .lake/build/bin/check-yaml ls .lake/packages/importGraph/.lake/build/bin/graph @@ -236,13 +235,11 @@ jobs: - name: update {Mathlib, Tactic, Counterexamples, Archive}.lean id: mk_all continue-on-error: true # Allow workflow to continue, outcome checked later - # This only runs `mk_all --check` from the `master-branch`, so doesn't need to be inside landrun - shell: bash + # This runs `mk_all --check` from the `pr-branch` inside landrun run: | cd pr-branch - - echo "Running mk_all --check (from master-branch)..." - LAKE_HOME="$TOOLCHAIN_DIR" ../master-branch/.lake/build/bin/mk_all --check + echo "Running mk_all --check (from pr-branch)..." + lake exe mk_all --check - name: begin gh-problem-match-wrap for build step uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 @@ -439,6 +436,7 @@ jobs: post_steps: name: Post-Build Step + if: github.repository == 'leanprover-community/mathlib4' needs: [build] runs-on: ubuntu-latest # Note these steps run on disposable GitHub runners, so no landrun sandboxing is needed. steps: @@ -520,6 +518,7 @@ jobs: style_lint: name: Lint style + if: github.repository == 'leanprover-community/mathlib4' runs-on: ubuntu-latest steps: - uses: leanprover-community/lint-style-action@f2e7272aad56233a642b08fe974cf09dd664b0c8 # 2025-05-22 @@ -527,13 +526,26 @@ jobs: mode: check lint-bib-file: true + build_and_lint: + name: CI Success + if: github.repository == 'leanprover-community/mathlib4' + needs: [style_lint, post_steps] + runs-on: ubuntu-latest + steps: + - name: succeed + run: | + echo "Success: Required build and lint checks completed!" + final: name: Post-CI job - if: true + if: github.repository == 'leanprover-community/mathlib4' needs: [style_lint, build, post_steps] runs-on: ubuntu-latest steps: - - id: PR + # This action is used to determine the PR metadata in the event of a push. + # If it is called from a PR from a fork, it will find nothing/irrelevant data. + - if: github.event_name != 'pull_request_target' + id: PR_from_push uses: 8BitJonny/gh-get-current-pr@08e737c57a3a4eb24cec6487664b243b77eb5e36 # 3.0.0 # TODO: this may not work properly if the same commit is pushed to multiple branches: # https://github.com/8BitJonny/gh-get-current-pr/issues/8 @@ -542,6 +554,13 @@ jobs: # Only return if PR is still open filterOutClosed: true + # Combine the output from the previous action with the metadata supplied by GitHub itself. + - id: PR + shell: bash + run: | + echo "number=${{ steps.PR_from_push.outputs.number || github.event.pull_request.number }}" >> $GITHUB_OUTPUT + echo "pr_labels=${{ steps.PR_from_push.outputs.pr_labels || join(github.event.pull_request.labels.*.name, ',') }}" >> $GITHUB_OUTPUT + - if: contains(steps.PR.outputs.pr_labels, 'bench-after-CI') name: If `bench-after-CI` is present, add a `!bench` comment. uses: GrantBirki/comment@608e41b19bc973020ec0e189ebfdae935d7fe0cc # v2.1.1 diff --git a/.github/workflows/build_fork.yml b/.github/workflows/build_fork.yml index 43246eef85a453..699f8fcb07086d 100644 --- a/.github/workflows/build_fork.yml +++ b/.github/workflows/build_fork.yml @@ -150,9 +150,8 @@ jobs: shell: bash # We're only building the `master` branch version of the tools, so don't need to run inside landrun. run: | cd master-branch - lake build cache mk_all check-yaml graph + lake build cache check-yaml graph ls .lake/build/bin/cache - ls .lake/build/bin/mk_all ls .lake/build/bin/check-yaml ls .lake/packages/importGraph/.lake/build/bin/graph @@ -233,13 +232,11 @@ jobs: - name: update {Mathlib, Tactic, Counterexamples, Archive}.lean id: mk_all continue-on-error: true # Allow workflow to continue, outcome checked later - # This only runs `mk_all --check` from the `master-branch`, so doesn't need to be inside landrun - shell: bash + # This runs `mk_all --check` from the `pr-branch` inside landrun run: | cd pr-branch - - echo "Running mk_all --check (from master-branch)..." - LAKE_HOME="$TOOLCHAIN_DIR" ../master-branch/.lake/build/bin/mk_all --check + echo "Running mk_all --check (from pr-branch)..." + lake exe mk_all --check - name: begin gh-problem-match-wrap for build step uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 @@ -436,6 +433,7 @@ jobs: post_steps: name: Post-Build Step (fork) + if: github.event.pull_request.head.repo.fork needs: [build] runs-on: ubuntu-latest # Note these steps run on disposable GitHub runners, so no landrun sandboxing is needed. steps: @@ -517,6 +515,7 @@ jobs: style_lint: name: Lint style (fork) + if: github.event.pull_request.head.repo.fork runs-on: ubuntu-latest steps: - uses: leanprover-community/lint-style-action@f2e7272aad56233a642b08fe974cf09dd664b0c8 # 2025-05-22 @@ -524,13 +523,26 @@ jobs: mode: check lint-bib-file: true + build_and_lint: + name: CI Success + if: github.event.pull_request.head.repo.fork + needs: [style_lint, post_steps] + runs-on: ubuntu-latest + steps: + - name: succeed + run: | + echo "Success: Required build and lint checks completed!" + final: name: Post-CI job (fork) if: github.event.pull_request.head.repo.fork needs: [style_lint, build, post_steps] runs-on: ubuntu-latest steps: - - id: PR + # This action is used to determine the PR metadata in the event of a push. + # If it is called from a PR from a fork, it will find nothing/irrelevant data. + - if: github.event_name != 'pull_request_target' + id: PR_from_push uses: 8BitJonny/gh-get-current-pr@08e737c57a3a4eb24cec6487664b243b77eb5e36 # 3.0.0 # TODO: this may not work properly if the same commit is pushed to multiple branches: # https://github.com/8BitJonny/gh-get-current-pr/issues/8 @@ -539,6 +551,13 @@ jobs: # Only return if PR is still open filterOutClosed: true + # Combine the output from the previous action with the metadata supplied by GitHub itself. + - id: PR + shell: bash + run: | + echo "number=${{ steps.PR_from_push.outputs.number || github.event.pull_request.number }}" >> $GITHUB_OUTPUT + echo "pr_labels=${{ steps.PR_from_push.outputs.pr_labels || join(github.event.pull_request.labels.*.name, ',') }}" >> $GITHUB_OUTPUT + - if: contains(steps.PR.outputs.pr_labels, 'bench-after-CI') name: If `bench-after-CI` is present, add a `!bench` comment. uses: GrantBirki/comment@608e41b19bc973020ec0e189ebfdae935d7fe0cc # v2.1.1 diff --git a/.github/workflows/build_status.yml b/.github/workflows/build_status.yml deleted file mode 100644 index bb2c17570bc91b..00000000000000 --- a/.github/workflows/build_status.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Build Status Check - -on: - workflow_run: - workflows: ["continuous integration", "continuous integration (mathlib forks)"] - types: - - completed - -jobs: - build-status: - name: Build Status - runs-on: ubuntu-latest - if: github.event.workflow_run.conclusion == 'success' - steps: - - name: Mark as successful - run: | - echo "Build workflow completed successfully" - echo "Workflow: ${{ github.event.workflow_run.name }}" - echo "Conclusion: ${{ github.event.workflow_run.conclusion }}" diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index cf6d576e9454ea..6889d945a5f983 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -15,6 +15,7 @@ env: jobs: check-lean4checker: runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' strategy: matrix: branch_type: [master, nightly] diff --git a/.github/workflows/dependent-issues.yml b/.github/workflows/dependent-issues.yml index ce61f7c43da1ec..7213bfd5890a18 100644 --- a/.github/workflows/dependent-issues.yml +++ b/.github/workflows/dependent-issues.yml @@ -9,6 +9,7 @@ jobs: cancel: name: 'Cancel Previous Runs (dependent issues)' runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' # timeout-minutes: 3 steps: - uses: styfle/cancel-workflow-action@85880fa0301c86cca9da44039ee3bb12d3bedbfa # 0.12.1 @@ -18,6 +19,7 @@ jobs: check: runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: - uses: z0al/dependent-issues@75d554cd9494b6e1766bc9d08a81c26444ad5c5a env: diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index d706cccdc1ba28..9ea2baf5d72d63 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -2,13 +2,14 @@ name: docker on: schedule: - cron: '0 0 * * *' # Runs at 00:00 UTC every day - workflow_dispatch: + workflow_dispatch: env: # Possibly change this to `hub.docker.com` in the future REGISTRY: ghcr.io REPO_NAME: ${{ github.repository }} jobs: build-and-push-docker-images: + if: github.repository == 'leanprover-community/mathlib4' strategy: fail-fast: false matrix: diff --git a/.github/workflows/label_new_contributor.yml b/.github/workflows/label_new_contributor.yml index 2b406c6f10e920..9b88d4e8cecbf3 100644 --- a/.github/workflows/label_new_contributor.yml +++ b/.github/workflows/label_new_contributor.yml @@ -4,6 +4,12 @@ name: Label New Contributors on: pull_request_target +# Limit permissions for GITHUB_TOKEN for the entire workflow +permissions: + contents: read + pull-requests: write # Only allow PR comments/labels + # All other permissions are implicitly 'none' + jobs: label-and-report-new-contributor: runs-on: ubuntu-latest diff --git a/.github/workflows/labels_from_comment.yml b/.github/workflows/labels_from_comment.yml index 6e895062b9c235..09891d14c8a570 100644 --- a/.github/workflows/labels_from_comment.yml +++ b/.github/workflows/labels_from_comment.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - name: Add label based on comment + - name: Add / remove label based on comment uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -25,6 +25,17 @@ jobs: const awaitingAuthor = commentLines.includes('awaiting-author'); const wip = commentLines.includes('WIP'); + const removeAwaitingAuthor = commentLines.includes('-awaiting-author'); + const removeWip = commentLines.includes('-WIP'); + + if (removeAwaitingAuthor) { + await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'awaiting-author' }).catch(() => {}); + } + + if (removeWip) { + await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'WIP' }).catch(() => {}); + } + if (awaitingAuthor || wip) { await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'awaiting-author' }).catch(() => {}); await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'WIP' }).catch(() => {}); diff --git a/.github/workflows/latest_import.yml b/.github/workflows/latest_import.yml index 26d6fa5ac0ed91..06675d9b05b182 100644 --- a/.github/workflows/latest_import.yml +++ b/.github/workflows/latest_import.yml @@ -12,6 +12,7 @@ jobs: late-importers: name: Build runs-on: pr + if: github.repository == 'leanprover-community/mathlib4' steps: - name: cleanup run: | diff --git a/.github/workflows/long_file_report.yml b/.github/workflows/long_file_report.yml index a358f3199435fb..0d9e70b56a631b 100644 --- a/.github/workflows/long_file_report.yml +++ b/.github/workflows/long_file_report.yml @@ -7,6 +7,7 @@ on: jobs: run-script: + if: github.repository == 'leanprover-community/mathlib4' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/maintainer_bors.yml b/.github/workflows/maintainer_bors.yml index d918c1d624a1b8..641357307be443 100644 --- a/.github/workflows/maintainer_bors.yml +++ b/.github/workflows/maintainer_bors.yml @@ -23,10 +23,19 @@ jobs: COMMENT_REVIEW: ${{ github.event.review.body }} name: Add ready-to-merge or delegated label runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: - name: Find bors merge/delegate id: merge_or_delegate run: | + # don't try to run if we don't have access to necessary secrets + if [[ -z '${{ secrets.TRIAGE_TOKEN }}' ]] + then + printf 'No access to secrets, aborting.' + printf 'mOrD=' > "${GITHUB_OUTPUT}" + exit 0 + fi + COMMENT="${COMMENT_EVENT}${COMMENT_REVIEW}" # we strip `\r` since line endings from GitHub contain this character COMMENT="${COMMENT//$'\r'/}" diff --git a/.github/workflows/maintainer_merge.yml b/.github/workflows/maintainer_merge.yml index f1ee93f19079a5..08d7df0cb9234c 100644 --- a/.github/workflows/maintainer_merge.yml +++ b/.github/workflows/maintainer_merge.yml @@ -29,10 +29,19 @@ jobs: EVENT_NAME: ${{ github.event_name }} name: Ping maintainers on Zulip runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: - name: Find maintainer merge/delegate id: merge_or_delegate run: | + # don't try to run if we don't have access to necessary secrets + if [[ -z '${{ secrets.MATHLIB_REVIEWERS_TEAM_KEY }}' ]] + then + printf 'No access to secrets, aborting.' + printf 'mOrD=' > "${GITHUB_OUTPUT}" + exit 0 + fi + echo "PR author: ${PR_AUTHOR}" COMMENT="${COMMENT_EVENT}${COMMENT_REVIEW}" diff --git a/.github/workflows/merge_conflicts.yml b/.github/workflows/merge_conflicts.yml index 28f34d79b8c72b..a1c607eacc834d 100644 --- a/.github/workflows/merge_conflicts.yml +++ b/.github/workflows/merge_conflicts.yml @@ -8,6 +8,7 @@ on: jobs: main: runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: - name: check if prs are dirty uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 diff --git a/.github/workflows/mk_build_yml.sh b/.github/workflows/mk_build_yml.sh index 2ab5d7ef2a335c..cae30b8662c66b 100755 --- a/.github/workflows/mk_build_yml.sh +++ b/.github/workflows/mk_build_yml.sh @@ -39,7 +39,7 @@ on: name: continuous integration EOF - include "github.sha" pr "true" "" ubuntu-latest + include "github.sha" pr "github.repository == 'leanprover-community\/mathlib4'" "" ubuntu-latest } bors_yml() { @@ -54,7 +54,7 @@ on: name: continuous integration (staging) EOF - include "github.sha" bors "true" "" bors + include "github.sha" bors "github.repository == 'leanprover-community\/mathlib4'" "" bors } build_fork_yml() { diff --git a/.github/workflows/nightly_bump_toolchain.yml b/.github/workflows/nightly_bump_toolchain.yml index e5371a3cd0e2ee..e6c604a8185a65 100644 --- a/.github/workflows/nightly_bump_toolchain.yml +++ b/.github/workflows/nightly_bump_toolchain.yml @@ -11,7 +11,7 @@ on: jobs: update-toolchain: runs-on: ubuntu-latest - + if: github.repository == 'leanprover-community/mathlib4' steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 diff --git a/.github/workflows/nightly_detect_failure.yml b/.github/workflows/nightly_detect_failure.yml index c07ebc3b08e613..46df4beef3d18d 100644 --- a/.github/workflows/nightly_detect_failure.yml +++ b/.github/workflows/nightly_detect_failure.yml @@ -8,7 +8,10 @@ on: jobs: handle_failure: - if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.head_branch == 'nightly-testing' }} + + if: ${{ github.repository == 'leanprover-community/mathlib4' && + github.event.workflow_run.conclusion == 'failure' && + github.event.workflow_run.head_branch == 'nightly-testing' }} runs-on: ubuntu-latest steps: diff --git a/.github/workflows/nightly_merge_master.yml b/.github/workflows/nightly_merge_master.yml index 231cbda16d4dcf..0c6b2ff6db3c9b 100644 --- a/.github/workflows/nightly_merge_master.yml +++ b/.github/workflows/nightly_merge_master.yml @@ -10,6 +10,7 @@ on: jobs: merge-to-nightly: runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 diff --git a/.github/workflows/nolints.yml b/.github/workflows/nolints.yml index dd817801e395d3..44a605bf24c173 100644 --- a/.github/workflows/nolints.yml +++ b/.github/workflows/nolints.yml @@ -9,6 +9,7 @@ jobs: build: name: Build, lint and update nolints and style exceptions runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: - name: cleanup run: | diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index df513a5cbd9879..71113ce27c9304 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,6 +7,7 @@ on: jobs: stale: runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: # until https://github.com/actions/stale/pull/1145 is merged - uses: asterisk/github-actions-stale@1b80269ed4fffa62ade5d212089f005f03cde943 # branch: main-only-matching-filter diff --git a/.github/workflows/technical_debt_metrics.yml b/.github/workflows/technical_debt_metrics.yml index 715c07cd631046..96f90f10bc0924 100644 --- a/.github/workflows/technical_debt_metrics.yml +++ b/.github/workflows/technical_debt_metrics.yml @@ -8,6 +8,7 @@ on: jobs: run-script: runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: - name: Checkout code diff --git a/.github/workflows/update_dependencies.yml b/.github/workflows/update_dependencies.yml index d8fda4a7faf290..75e945d5780108 100644 --- a/.github/workflows/update_dependencies.yml +++ b/.github/workflows/update_dependencies.yml @@ -8,6 +8,7 @@ on: jobs: update-dependencies: runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 diff --git a/.github/workflows/update_dependencies_zulip.yml b/.github/workflows/update_dependencies_zulip.yml index b57f04ebaf862e..8dbe0fa74ce00a 100644 --- a/.github/workflows/update_dependencies_zulip.yml +++ b/.github/workflows/update_dependencies_zulip.yml @@ -11,7 +11,7 @@ on: jobs: monitor-failures: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'failure' }} + if: ${{ github.repository == 'leanprover-community/mathlib4' && github.event.workflow_run.conclusion == 'failure' }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 diff --git a/.github/workflows/zulip_emoji_closed_pr.yaml b/.github/workflows/zulip_emoji_closed_pr.yaml index 304acbe0e891a9..053ac948280f2b 100644 --- a/.github/workflows/zulip_emoji_closed_pr.yaml +++ b/.github/workflows/zulip_emoji_closed_pr.yaml @@ -3,10 +3,15 @@ name: Add "closed-pr" emoji in Zulip # triggers the action when on: - pull_request: + pull_request_target: # the pull request is closed or reopened, to add or remove the emoji types: [closed, reopened] +# Limit permissions for GITHUB_TOKEN for the entire workflow +permissions: + contents: read # minimum permissions for actions/checkout & actions/setup-python + # All other permissions are implicitly 'none' + jobs: add_closed_pr_emoji: # we set the `TITLE` of the PR as a variable, this shields from possible code injection @@ -15,6 +20,7 @@ jobs: name: Add closed-pr emoji in Zulip runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: - name: Debugging information run: | @@ -42,6 +48,7 @@ jobs: github.event_name == 'reopened' }} uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: + ref: master sparse-checkout: | scripts/zulip_emoji_reactions.py diff --git a/.github/workflows/zulip_emoji_labelling.yaml b/.github/workflows/zulip_emoji_labelling.yaml index a37d4e40cc155d..8cee6d08a40f9b 100644 --- a/.github/workflows/zulip_emoji_labelling.yaml +++ b/.github/workflows/zulip_emoji_labelling.yaml @@ -1,16 +1,24 @@ on: - pull_request: + pull_request_target: types: [labeled, unlabeled] + +# Limit permissions for GITHUB_TOKEN for the entire workflow +permissions: + contents: read # minimum permissions for actions/checkout & actions/setup-python + # All other permissions are implicitly 'none' + jobs: # When a PR is (un)labelled with awaiting-author or maintainer-merge, # add resp. remove the matching emoji reaction from zulip messages. set_pr_emoji: - if: github.event.label.name == 'awaiting-author' || github.event.label.name == 'maintainer-merge' + if: (github.event.label.name == 'awaiting-author' || github.event.label.name == 'maintainer-merge') && + github.repository == 'leanprover-community/mathlib4' runs-on: ubuntu-latest steps: - name: Checkout mathlib repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: + ref: master sparse-checkout: | scripts/zulip_emoji_reactions.py diff --git a/.github/workflows/zulip_emoji_merge_delegate.yaml b/.github/workflows/zulip_emoji_merge_delegate.yaml index 30c2c1aa0badef..73d76b6d3fcf15 100644 --- a/.github/workflows/zulip_emoji_merge_delegate.yaml +++ b/.github/workflows/zulip_emoji_merge_delegate.yaml @@ -9,6 +9,7 @@ on: jobs: zulip-emoji-merged: runs-on: ubuntu-latest + if: github.repository == 'leanprover-community/mathlib4' steps: - name: Checkout mathlib repository diff --git a/Cache/Hashing.lean b/Cache/Hashing.lean index 289a9444e03758..418e884d1262cd 100644 --- a/Cache/Hashing.lean +++ b/Cache/Hashing.lean @@ -91,9 +91,6 @@ Computes the root hash, which mixes the hashes of the content of: * `lake-manifest.json` and the hash of `Lean.githash`. - -(We hash `Lean.githash` in case the toolchain changes even though `lean-toolchain` hasn't. -This happens with the `lean-pr-testing-NNNN` toolchains when Lean 4 PRs are updated.) -/ def getRootHash : CacheM UInt64 := do let mathlibDepPath := (← read).mathlibDepPath @@ -103,7 +100,7 @@ def getRootHash : CacheM UInt64 := do mathlibDepPath / "lake-manifest.json"] let hashes ← rootFiles.mapM fun path => hashFileContents <$> IO.FS.readFile path - return hash (rootHashGeneration :: hash Lean.githash :: hashes) + return hash (rootHashGeneration :: hashes) /-- Computes the hash of a file, which mixes: diff --git a/Cache/Requests.lean b/Cache/Requests.lean index 6d74222562236f..3c313509356579 100644 --- a/Cache/Requests.lean +++ b/Cache/Requests.lean @@ -229,9 +229,9 @@ def getFiles else let repo ← getRemoteRepo (← read).mathlibDepPath IO.println s!"Mathlib repository: {repo}" - downloadFiles repo hashMap forceDownload parallel (warnOnMissing := false) unless repo == MATHLIBREPO do - downloadFiles MATHLIBREPO hashMap forceDownload parallel (warnOnMissing := true) + downloadFiles MATHLIBREPO hashMap forceDownload parallel (warnOnMissing := false) + downloadFiles repo hashMap forceDownload parallel (warnOnMissing := true) if decompress then IO.unpackCache hashMap forceUnpack else diff --git a/Counterexamples/AharoniKorman.lean b/Counterexamples/AharoniKorman.lean index aabd9a0a00d8e4..cb3f86cd6df8b2 100644 --- a/Counterexamples/AharoniKorman.lean +++ b/Counterexamples/AharoniKorman.lean @@ -93,7 +93,7 @@ open Hollom /-- `toHollom` and `ofHollom` as an equivalence. -/ @[simps] def equivHollom : ℕ × ℕ × ℕ ≃ Hollom where - toFun := toHollom; invFun := ofHollom; left_inv _ := rfl; right_inv _ := rfl + toFun := toHollom; invFun := ofHollom namespace Hollom diff --git a/Mathlib.lean b/Mathlib.lean index c985dd059537e6..b45288c9129427 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -468,6 +468,7 @@ import Mathlib.Algebra.GroupWithZero.Pointwise.Finset import Mathlib.Algebra.GroupWithZero.Pointwise.Set.Basic import Mathlib.Algebra.GroupWithZero.Pointwise.Set.Card import Mathlib.Algebra.GroupWithZero.Prod +import Mathlib.Algebra.GroupWithZero.ProdHom import Mathlib.Algebra.GroupWithZero.Semiconj import Mathlib.Algebra.GroupWithZero.Subgroup import Mathlib.Algebra.GroupWithZero.Submonoid.Pointwise @@ -724,6 +725,7 @@ import Mathlib.Algebra.MvPolynomial.Expand import Mathlib.Algebra.MvPolynomial.Funext import Mathlib.Algebra.MvPolynomial.Invertible import Mathlib.Algebra.MvPolynomial.Monad +import Mathlib.Algebra.MvPolynomial.Nilpotent import Mathlib.Algebra.MvPolynomial.PDeriv import Mathlib.Algebra.MvPolynomial.Polynomial import Mathlib.Algebra.MvPolynomial.Rename @@ -1030,6 +1032,7 @@ import Mathlib.Algebra.Ring.Fin import Mathlib.Algebra.Ring.GrindInstances import Mathlib.Algebra.Ring.Hom.Basic import Mathlib.Algebra.Ring.Hom.Defs +import Mathlib.Algebra.Ring.Hom.InjSurj import Mathlib.Algebra.Ring.Idempotent import Mathlib.Algebra.Ring.Identities import Mathlib.Algebra.Ring.InjSurj @@ -1486,11 +1489,10 @@ import Mathlib.Analysis.Convex.Birkhoff import Mathlib.Analysis.Convex.Body import Mathlib.Analysis.Convex.Caratheodory import Mathlib.Analysis.Convex.Combination +import Mathlib.Analysis.Convex.Cone.Basic import Mathlib.Analysis.Convex.Cone.Closure import Mathlib.Analysis.Convex.Cone.Extension import Mathlib.Analysis.Convex.Cone.InnerDual -import Mathlib.Analysis.Convex.Cone.Pointed -import Mathlib.Analysis.Convex.Cone.Proper import Mathlib.Analysis.Convex.Continuous import Mathlib.Analysis.Convex.Contractible import Mathlib.Analysis.Convex.Deriv @@ -1755,6 +1757,7 @@ import Mathlib.Analysis.RCLike.Basic import Mathlib.Analysis.RCLike.BoundedContinuous import Mathlib.Analysis.RCLike.Inner import Mathlib.Analysis.RCLike.Lemmas +import Mathlib.Analysis.RCLike.TangentCone import Mathlib.Analysis.Seminorm import Mathlib.Analysis.SpecialFunctions.Arsinh import Mathlib.Analysis.SpecialFunctions.Bernstein @@ -2044,6 +2047,7 @@ import Mathlib.CategoryTheory.FiberedCategory.Cartesian import Mathlib.CategoryTheory.FiberedCategory.Cocartesian import Mathlib.CategoryTheory.FiberedCategory.Fiber import Mathlib.CategoryTheory.FiberedCategory.Fibered +import Mathlib.CategoryTheory.FiberedCategory.HasFibers import Mathlib.CategoryTheory.FiberedCategory.HomLift import Mathlib.CategoryTheory.Filtered.Basic import Mathlib.CategoryTheory.Filtered.Connected @@ -2135,6 +2139,7 @@ import Mathlib.CategoryTheory.Join.Sum import Mathlib.CategoryTheory.LiftingProperties.Adjunction import Mathlib.CategoryTheory.LiftingProperties.Basic import Mathlib.CategoryTheory.LiftingProperties.Limits +import Mathlib.CategoryTheory.LiftingProperties.ParametrizedAdjunction import Mathlib.CategoryTheory.Limits.Bicones import Mathlib.CategoryTheory.Limits.ColimitLimit import Mathlib.CategoryTheory.Limits.Comma @@ -2196,6 +2201,7 @@ import Mathlib.CategoryTheory.Limits.Over import Mathlib.CategoryTheory.Limits.Pi import Mathlib.CategoryTheory.Limits.Preorder import Mathlib.CategoryTheory.Limits.Preserves.Basic +import Mathlib.CategoryTheory.Limits.Preserves.Bifunctor import Mathlib.CategoryTheory.Limits.Preserves.Creates.Finite import Mathlib.CategoryTheory.Limits.Preserves.Filtered import Mathlib.CategoryTheory.Limits.Preserves.Finite @@ -2279,6 +2285,7 @@ import Mathlib.CategoryTheory.Limits.Shapes.ZeroMorphisms import Mathlib.CategoryTheory.Limits.Shapes.ZeroObjects import Mathlib.CategoryTheory.Limits.Sifted import Mathlib.CategoryTheory.Limits.SmallComplete +import Mathlib.CategoryTheory.Limits.Types.ColimitType import Mathlib.CategoryTheory.Limits.Types.Colimits import Mathlib.CategoryTheory.Limits.Types.Filtered import Mathlib.CategoryTheory.Limits.Types.Images @@ -2323,6 +2330,7 @@ import Mathlib.CategoryTheory.Localization.SmallShiftedHom import Mathlib.CategoryTheory.Localization.StructuredArrow import Mathlib.CategoryTheory.Localization.Triangulated import Mathlib.CategoryTheory.Localization.Trifunctor +import Mathlib.CategoryTheory.LocallyDirected import Mathlib.CategoryTheory.Monad.Adjunction import Mathlib.CategoryTheory.Monad.Algebra import Mathlib.CategoryTheory.Monad.Basic @@ -3374,6 +3382,7 @@ import Mathlib.Data.Set.Constructions import Mathlib.Data.Set.Countable import Mathlib.Data.Set.Defs import Mathlib.Data.Set.Disjoint +import Mathlib.Data.Set.Dissipate import Mathlib.Data.Set.Enumerate import Mathlib.Data.Set.Equitable import Mathlib.Data.Set.Finite.Basic @@ -3479,6 +3488,7 @@ import Mathlib.Deprecated.Cardinal.Continuum import Mathlib.Deprecated.Cardinal.Finite import Mathlib.Deprecated.Cardinal.PartENat import Mathlib.Deprecated.Order +import Mathlib.Deprecated.RingHom import Mathlib.Dynamics.BirkhoffSum.Average import Mathlib.Dynamics.BirkhoffSum.Basic import Mathlib.Dynamics.BirkhoffSum.NormedSpace @@ -3576,6 +3586,7 @@ import Mathlib.FieldTheory.SplittingField.Construction import Mathlib.FieldTheory.SplittingField.IsSplittingField import Mathlib.FieldTheory.Tower import Mathlib.Geometry.Convex.Cone.Basic +import Mathlib.Geometry.Convex.Cone.Pointed import Mathlib.Geometry.Euclidean.Altitude import Mathlib.Geometry.Euclidean.Angle.Oriented.Affine import Mathlib.Geometry.Euclidean.Angle.Oriented.Basic @@ -3589,6 +3600,7 @@ import Mathlib.Geometry.Euclidean.Angle.Unoriented.CrossProduct import Mathlib.Geometry.Euclidean.Angle.Unoriented.RightAngle import Mathlib.Geometry.Euclidean.Basic import Mathlib.Geometry.Euclidean.Circumcenter +import Mathlib.Geometry.Euclidean.Incenter import Mathlib.Geometry.Euclidean.Inversion.Basic import Mathlib.Geometry.Euclidean.Inversion.Calculus import Mathlib.Geometry.Euclidean.Inversion.ImageHyperplane @@ -3832,8 +3844,8 @@ import Mathlib.Lean.Meta.KAbstractPositions import Mathlib.Lean.Meta.RefinedDiscrTree import Mathlib.Lean.Meta.RefinedDiscrTree.Basic import Mathlib.Lean.Meta.RefinedDiscrTree.Encode +import Mathlib.Lean.Meta.RefinedDiscrTree.Initialize import Mathlib.Lean.Meta.RefinedDiscrTree.Lookup -import Mathlib.Lean.Meta.RefinedDiscrTree.Pi import Mathlib.Lean.Meta.Simp import Mathlib.Lean.Name import Mathlib.Lean.PrettyPrinter.Delaborator @@ -4325,6 +4337,7 @@ import Mathlib.MeasureTheory.Integral.Periodic import Mathlib.MeasureTheory.Integral.Pi import Mathlib.MeasureTheory.Integral.Prod import Mathlib.MeasureTheory.Integral.RieszMarkovKakutani.Basic +import Mathlib.MeasureTheory.Integral.RieszMarkovKakutani.NNReal import Mathlib.MeasureTheory.Integral.RieszMarkovKakutani.Real import Mathlib.MeasureTheory.Integral.SetIntegral import Mathlib.MeasureTheory.Integral.SetToL1 @@ -4554,6 +4567,7 @@ import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Basic import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Defs import Mathlib.NumberTheory.ModularForms.EisensteinSeries.IsBoundedAtImInfty import Mathlib.NumberTheory.ModularForms.EisensteinSeries.MDifferentiable +import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Summable import Mathlib.NumberTheory.ModularForms.EisensteinSeries.UniformConvergence import Mathlib.NumberTheory.ModularForms.Identities import Mathlib.NumberTheory.ModularForms.JacobiTheta.Bounds @@ -4581,6 +4595,7 @@ import Mathlib.NumberTheory.NumberField.Completion import Mathlib.NumberTheory.NumberField.DedekindZeta import Mathlib.NumberTheory.NumberField.Discriminant.Basic import Mathlib.NumberTheory.NumberField.Discriminant.Defs +import Mathlib.NumberTheory.NumberField.Discriminant.Different import Mathlib.NumberTheory.NumberField.Embeddings import Mathlib.NumberTheory.NumberField.EquivReindex import Mathlib.NumberTheory.NumberField.FinitePlaces @@ -4688,6 +4703,7 @@ import Mathlib.Order.CompleteLattice.Basic import Mathlib.Order.CompleteLattice.Chain import Mathlib.Order.CompleteLattice.Defs import Mathlib.Order.CompleteLattice.Finset +import Mathlib.Order.CompleteLattice.Group import Mathlib.Order.CompleteLattice.Lemmas import Mathlib.Order.CompleteLattice.SetLike import Mathlib.Order.CompleteLatticeIntervals @@ -5349,6 +5365,7 @@ import Mathlib.RingTheory.Polynomial.Basic import Mathlib.RingTheory.Polynomial.Bernstein import Mathlib.RingTheory.Polynomial.Chebyshev import Mathlib.RingTheory.Polynomial.Content +import Mathlib.RingTheory.Polynomial.ContentIdeal import Mathlib.RingTheory.Polynomial.Cyclotomic.Basic import Mathlib.RingTheory.Polynomial.Cyclotomic.Eval import Mathlib.RingTheory.Polynomial.Cyclotomic.Expand @@ -5704,7 +5721,6 @@ import Mathlib.Tactic.FunProp.Differentiable import Mathlib.Tactic.FunProp.Elab import Mathlib.Tactic.FunProp.FunctionData import Mathlib.Tactic.FunProp.Mor -import Mathlib.Tactic.FunProp.StateList import Mathlib.Tactic.FunProp.Theorems import Mathlib.Tactic.FunProp.ToBatteries import Mathlib.Tactic.FunProp.Types @@ -5712,6 +5728,9 @@ import Mathlib.Tactic.GCongr import Mathlib.Tactic.GCongr.Core import Mathlib.Tactic.GCongr.CoreAttrs import Mathlib.Tactic.GCongr.ForwardAttr +import Mathlib.Tactic.GRewrite +import Mathlib.Tactic.GRewrite.Core +import Mathlib.Tactic.GRewrite.Elab import Mathlib.Tactic.Generalize import Mathlib.Tactic.GeneralizeProofs import Mathlib.Tactic.Group @@ -5848,6 +5867,7 @@ import Mathlib.Tactic.SetLike import Mathlib.Tactic.SimpIntro import Mathlib.Tactic.SimpRw import Mathlib.Tactic.Simproc.ExistsAndEq +import Mathlib.Tactic.Simproc.Factors import Mathlib.Tactic.Simps.Basic import Mathlib.Tactic.Simps.NotationClass import Mathlib.Tactic.SplitIfs @@ -5993,6 +6013,8 @@ import Mathlib.Topology.Algebra.ProperAction.Basic import Mathlib.Topology.Algebra.ProperAction.ProperlyDiscontinuous import Mathlib.Topology.Algebra.ProperConstSMul import Mathlib.Topology.Algebra.RestrictedProduct +import Mathlib.Topology.Algebra.RestrictedProduct.Basic +import Mathlib.Topology.Algebra.RestrictedProduct.TopologicalSpace import Mathlib.Topology.Algebra.Ring.Basic import Mathlib.Topology.Algebra.Ring.Compact import Mathlib.Topology.Algebra.Ring.Ideal @@ -6104,6 +6126,7 @@ import Mathlib.Topology.Compactification.OnePoint.Sphere import Mathlib.Topology.Compactification.OnePointEquiv import Mathlib.Topology.Compactness.Bases import Mathlib.Topology.Compactness.Compact +import Mathlib.Topology.Compactness.CompactSystem import Mathlib.Topology.Compactness.CompactlyCoherentSpace import Mathlib.Topology.Compactness.CompactlyGeneratedSpace import Mathlib.Topology.Compactness.DeltaGeneratedSpace @@ -6118,6 +6141,7 @@ import Mathlib.Topology.Connected.Basic import Mathlib.Topology.Connected.Clopen import Mathlib.Topology.Connected.LocPathConnected import Mathlib.Topology.Connected.LocallyConnected +import Mathlib.Topology.Connected.PathComponentOne import Mathlib.Topology.Connected.PathConnected import Mathlib.Topology.Connected.Separation import Mathlib.Topology.Connected.TotallyDisconnected @@ -6365,6 +6389,7 @@ import Mathlib.Topology.Separation.Regular import Mathlib.Topology.Separation.SeparatedNhds import Mathlib.Topology.Sequences import Mathlib.Topology.Sets.Closeds +import Mathlib.Topology.Sets.CompactOpenCovered import Mathlib.Topology.Sets.Compacts import Mathlib.Topology.Sets.OpenCover import Mathlib.Topology.Sets.Opens diff --git a/Mathlib/Algebra/AddConstMap/Equiv.lean b/Mathlib/Algebra/AddConstMap/Equiv.lean index 754428fdd0f20d..10078846e117a5 100644 --- a/Mathlib/Algebra/AddConstMap/Equiv.lean +++ b/Mathlib/Algebra/AddConstMap/Equiv.lean @@ -97,6 +97,16 @@ lemma self_trans_symm (e : G ≃+c[a, b] H) : e.trans e.symm = .refl a := lemma symm_trans_self (e : G ≃+c[a, b] H) : e.symm.trans e = .refl b := toEquiv_injective e.toEquiv.symm_trans_self +@[simp] +lemma coe_symm_toEquiv (e : G ≃+c[a, b] H) : ⇑e.toEquiv.symm = e.symm := rfl + +@[simp] +lemma toEquiv_symm (e : G ≃+c[a, b] H) : e.symm.toEquiv = e.toEquiv.symm := rfl + +@[simp] +lemma toEquiv_trans (e₁ : G ≃+c[a, b] H) (e₂ : H ≃+c[b, c] K) : + (e₁.trans e₂).toEquiv = e₁.toEquiv.trans e₂.toEquiv := rfl + instance instOne : One (G ≃+c[a, a] G) := ⟨.refl _⟩ instance instMul : Mul (G ≃+c[a, a] G) := ⟨fun f g ↦ g.trans f⟩ instance instInv : Inv (G ≃+c[a, a] G) := ⟨.symm⟩ @@ -134,8 +144,6 @@ def equivUnits : (G ≃+c[a, a] G) ≃* (G →+c[a, a] G)ˣ where invFun u := { toEquiv := Equiv.Perm.equivUnitsEnd.symm <| Units.map AddConstMap.toEnd u map_add_const' := u.1.2 } - left_inv _ := rfl - right_inv _ := rfl map_mul' _ _ := rfl end AddConstEquiv diff --git a/Mathlib/Algebra/Algebra/Basic.lean b/Mathlib/Algebra/Algebra/Basic.lean index 16e299bca327ba..c8228b477a0c38 100644 --- a/Mathlib/Algebra/Algebra/Basic.lean +++ b/Mathlib/Algebra/Algebra/Basic.lean @@ -540,8 +540,6 @@ def LinearMap.extendScalarsOfSurjectiveEquiv (h : Surjective (algebraMap R S)) : map_add' _ _ := rfl map_smul' _ _ := rfl invFun f := f.restrictScalars S - left_inv _ := rfl - right_inv _ := rfl /-- If `R →+* S` is surjective, then `R`-linear maps are also `S`-linear. -/ abbrev LinearMap.extendScalarsOfSurjective (h : Surjective (algebraMap R S)) diff --git a/Mathlib/Algebra/Algebra/Equiv.lean b/Mathlib/Algebra/Algebra/Equiv.lean index dca97b4d9e990c..bd753b4f57f9e2 100644 --- a/Mathlib/Algebra/Algebra/Equiv.lean +++ b/Mathlib/Algebra/Algebra/Equiv.lean @@ -495,9 +495,15 @@ theorem toLinearEquiv_refl : (AlgEquiv.refl : A₁ ≃ₐ[R] A₁).toLinearEquiv rfl @[simp] -theorem toLinearEquiv_symm (e : A₁ ≃ₐ[R] A₂) : e.toLinearEquiv.symm = e.symm.toLinearEquiv := +theorem toLinearEquiv_symm (e : A₁ ≃ₐ[R] A₂) : e.symm.toLinearEquiv = e.toLinearEquiv.symm := rfl +@[simp] +theorem coe_toLinearEquiv (e : A₁ ≃ₐ[R] A₂) : ⇑e.toLinearEquiv = e := rfl + +@[simp] +theorem coe_symm_toLinearEquiv (e : A₁ ≃ₐ[R] A₂) : ⇑e.toLinearEquiv.symm = e.symm := rfl + @[simp] theorem toLinearEquiv_trans (e₁ : A₁ ≃ₐ[R] A₂) (e₂ : A₂ ≃ₐ[R] A₃) : (e₁.trans e₂).toLinearEquiv = e₁.toLinearEquiv.trans e₂.toLinearEquiv := @@ -707,8 +713,6 @@ def algHomUnitsEquiv (R S : Type*) [CommSemiring R] [Semiring S] [Algebra R S] : left_inv := (fun x ↦ show (↑(f⁻¹ * f) : S →ₐ[R] S) x = x by rw [inv_mul_cancel]; rfl) right_inv := (fun x ↦ show (↑(f * f⁻¹) : S →ₐ[R] S) x = x by rw [mul_inv_cancel]; rfl) } invFun := fun f ↦ ⟨f, f.symm, f.comp_symm, f.symm_comp⟩ - left_inv := fun _ ↦ rfl - right_inv := fun _ ↦ rfl map_mul' := fun _ _ ↦ rfl /-- See also `Finite.algHom` -/ diff --git a/Mathlib/Algebra/Algebra/NonUnitalHom.lean b/Mathlib/Algebra/Algebra/NonUnitalHom.lean index 41668896b20765..826e3a1291a2da 100644 --- a/Mathlib/Algebra/Algebra/NonUnitalHom.lean +++ b/Mathlib/Algebra/Algebra/NonUnitalHom.lean @@ -395,8 +395,6 @@ their codomains. -/ def prodEquiv : (A →ₙₐ[R] B) × (A →ₙₐ[R] C) ≃ (A →ₙₐ[R] B × C) where toFun f := f.1.prod f.2 invFun f := ((fst _ _ _).comp f, (snd _ _ _).comp f) - left_inv _ := rfl - right_inv _ := rfl variable (R A B) diff --git a/Mathlib/Algebra/Algebra/Operations.lean b/Mathlib/Algebra/Algebra/Operations.lean index 476527349fcea8..cce60fba9af260 100644 --- a/Mathlib/Algebra/Algebra/Operations.lean +++ b/Mathlib/Algebra/Algebra/Operations.lean @@ -565,6 +565,10 @@ instance idemSemiring : IdemSemiring (Submodule R A) where mul_one := Submodule.mul_one bot_le _ := bot_le +instance : IsOrderedRing (Submodule R A) where + mul_le_mul_of_nonneg_left _ _ _ h _ := mul_le_mul_left' h _ + mul_le_mul_of_nonneg_right _ _ _ h _ := mul_le_mul_right' h _ + variable (M) theorem span_pow (s : Set A) : ∀ n : ℕ, span R s ^ n = span R (s ^ n) diff --git a/Mathlib/Algebra/Algebra/Opposite.lean b/Mathlib/Algebra/Algebra/Opposite.lean index b693ec7d6a84e6..61b3fb84fe1f22 100644 --- a/Mathlib/Algebra/Algebra/Opposite.lean +++ b/Mathlib/Algebra/Algebra/Opposite.lean @@ -110,8 +110,6 @@ This is the action of the (fully faithful) `ᵐᵒᵖ`-functor on morphisms. -/ protected def op : (A →ₐ[R] B) ≃ (Aᵐᵒᵖ →ₐ[R] Bᵐᵒᵖ) where toFun f := { RingHom.op f.toRingHom with commutes' := fun r => unop_injective <| f.commutes r } invFun f := { RingHom.unop f.toRingHom with commutes' := fun r => op_injective <| f.commutes r } - left_inv _f := AlgHom.ext fun _a => rfl - right_inv _f := AlgHom.ext fun _a => rfl theorem toRingHom_op (f : A →ₐ[R] B) : f.op.toRingHom = RingHom.op f.toRingHom := rfl @@ -141,8 +139,6 @@ def op : (A ≃ₐ[R] B) ≃ Aᵐᵒᵖ ≃ₐ[R] Bᵐᵒᵖ where invFun f := { RingEquiv.unop f.toRingEquiv with commutes' := fun r => MulOpposite.op_injective <| f.commutes r } - left_inv _f := AlgEquiv.ext fun _a => rfl - right_inv _f := AlgEquiv.ext fun _a => rfl theorem toAlgHom_op (f : A ≃ₐ[R] B) : (AlgEquiv.op f).toAlgHom = AlgHom.op f.toAlgHom := diff --git a/Mathlib/Algebra/Algebra/Prod.lean b/Mathlib/Algebra/Algebra/Prod.lean index c9b56d44cecda3..995eff24345e11 100644 --- a/Mathlib/Algebra/Algebra/Prod.lean +++ b/Mathlib/Algebra/Algebra/Prod.lean @@ -103,8 +103,6 @@ their codomains. -/ def prodEquiv : (A →ₐ[R] B) × (A →ₐ[R] C) ≃ (A →ₐ[R] B × C) where toFun f := f.1.prod f.2 invFun f := ((fst _ _ _).comp f, (snd _ _ _).comp f) - left_inv f := by ext <;> rfl - right_inv f := by ext <;> rfl /-- `Prod.map` of two algebra homomorphisms. -/ def prodMap {D : Type*} [Semiring D] [Algebra R D] (f : A →ₐ[R] B) (g : C →ₐ[R] D) : diff --git a/Mathlib/Algebra/Algebra/Spectrum/Pi.lean b/Mathlib/Algebra/Algebra/Spectrum/Pi.lean index e4c5792e60246c..38fb258fd42e55 100644 --- a/Mathlib/Algebra/Algebra/Spectrum/Pi.lean +++ b/Mathlib/Algebra/Algebra/Spectrum/Pi.lean @@ -40,8 +40,6 @@ def PreQuasiregular.toPi [∀ i, NonUnitalSemiring (κ i)] : PreQuasiregular (∀ i, κ i) ≃* ∀ i, PreQuasiregular (κ i) where toFun := fun x i => .mk <| x.val i invFun := fun x => .mk <| fun i => (x i).val - left_inv _ := rfl - right_inv _ := rfl map_mul' _ _ := rfl variable (A B) in @@ -51,8 +49,6 @@ def PreQuasiregular.toProd [NonUnitalSemiring A] [NonUnitalSemiring B] : PreQuasiregular (A × B) ≃* PreQuasiregular A × PreQuasiregular B where toFun := fun p => ⟨.mk p.val.1, .mk p.val.2⟩ invFun := fun ⟨a, b⟩ => .mk ⟨a.val, b.val⟩ - left_inv _ := rfl - right_inv _ := rfl map_mul' _ _ := rfl lemma isQuasiregular_pi_iff [∀ i, NonUnitalSemiring (κ i)] (x : ∀ i, κ i) : diff --git a/Mathlib/Algebra/Algebra/Spectrum/Quasispectrum.lean b/Mathlib/Algebra/Algebra/Spectrum/Quasispectrum.lean index aa728c7bb74463..7d9ee69445e410 100644 --- a/Mathlib/Algebra/Algebra/Spectrum/Quasispectrum.lean +++ b/Mathlib/Algebra/Algebra/Spectrum/Quasispectrum.lean @@ -77,8 +77,6 @@ variable {R : Type*} [NonUnitalSemiring R] def equiv : R ≃ PreQuasiregular R where toFun := .mk invFun := PreQuasiregular.val - left_inv _ := rfl - right_inv _ := rfl instance instOne : One (PreQuasiregular R) where one := equiv 0 diff --git a/Mathlib/Algebra/BigOperators/Associated.lean b/Mathlib/Algebra/BigOperators/Associated.lean index d90fa9b0877ab3..cf982878c72fc8 100644 --- a/Mathlib/Algebra/BigOperators/Associated.lean +++ b/Mathlib/Algebra/BigOperators/Associated.lean @@ -97,16 +97,15 @@ theorem divisor_closure_eq_closure [CancelCommMonoidWithZero α] · rcases ha₁.exists_right_inv with ⟨k, hk⟩ refine hind x (y*k) ?_ hs ?_ · simp only [← mul_assoc, ← hprod, ← Multiset.prod_cons, mul_comm] - refine multiset_prod_mem _ _ (Multiset.forall_mem_cons.2 ⟨subset_closure (Set.mem_def.2 ?_), - Multiset.forall_mem_cons.2 ⟨subset_closure (Set.mem_def.2 ?_), (fun t ht => - subset_closure (hs t ht))⟩⟩) + refine multiset_prod_mem _ _ (Multiset.forall_mem_cons.2 ⟨subset_closure ?_, + Multiset.forall_mem_cons.2 ⟨subset_closure ?_, fun t ht => subset_closure (hs t ht)⟩⟩) · left; exact isUnit_of_mul_eq_one_right _ _ hk · left; exact ha₁ · rw [← mul_one s.prod, ← hk, ← mul_assoc, ← mul_assoc, mul_eq_mul_right_iff, mul_comm] left; exact hprod · rcases ha₂.dvd_mul.1 (Dvd.intro _ hprod) with ⟨c, hc⟩ | ⟨c, hc⟩ · rw [hc]; rw [hc, mul_assoc] at hprod - refine Submonoid.mul_mem _ (subset_closure (Set.mem_def.2 ?_)) + refine Submonoid.mul_mem _ (subset_closure ?_) (hind _ _ ?_ hs (mul_left_cancel₀ ha₂.ne_zero hprod)) · right; exact ha₂ rw [← mul_left_cancel₀ ha₂.ne_zero hprod] diff --git a/Mathlib/Algebra/BigOperators/Field.lean b/Mathlib/Algebra/BigOperators/Field.lean index 50ec1383de7bfc..9859dc84dbdb55 100644 --- a/Mathlib/Algebra/BigOperators/Field.lean +++ b/Mathlib/Algebra/BigOperators/Field.lean @@ -40,8 +40,7 @@ lemma dens_biUnion [DecidableEq β] (h : s.toSet.PairwiseDisjoint t) : lemma dens_biUnion_le [DecidableEq β] : (s.biUnion t).dens ≤ ∑ a ∈ s, (t a).dens := by simp only [dens, ← sum_div] gcongr - · positivity - · exact mod_cast card_biUnion_le + exact mod_cast card_biUnion_le lemma dens_eq_sum_dens_fiberwise [DecidableEq α] {f : β → α} {t : Finset β} (h : t.toSet.MapsTo f s) : t.dens = ∑ a ∈ s, {b ∈ t | f b = a}.dens := by diff --git a/Mathlib/Algebra/BigOperators/Finprod.lean b/Mathlib/Algebra/BigOperators/Finprod.lean index e1322739961fe4..8f99ddd74f442c 100644 --- a/Mathlib/Algebra/BigOperators/Finprod.lean +++ b/Mathlib/Algebra/BigOperators/Finprod.lean @@ -564,7 +564,7 @@ theorem finprod_mem_one (s : Set α) : (∏ᶠ i ∈ s, (1 : M)) = 1 := by simp /-- If a function `f` equals `1` on a set `s`, then the product of `f i` over `i ∈ s` equals `1`. -/ @[to_additive - "If a function `f` equals `0` on a set `s`, then the product of `f i` over `i ∈ s` + "If a function `f` equals `0` on a set `s`, then the sum of `f i` over `i ∈ s` equals `0`."] theorem finprod_mem_of_eqOn_one (hf : s.EqOn f 1) : ∏ᶠ i ∈ s, f i = 1 := by rw [← finprod_mem_one s] diff --git a/Mathlib/Algebra/BigOperators/Group/Finset/Basic.lean b/Mathlib/Algebra/BigOperators/Group/Finset/Basic.lean index a58ef964a23ec0..c5c6be49761d61 100644 --- a/Mathlib/Algebra/BigOperators/Group/Finset/Basic.lean +++ b/Mathlib/Algebra/BigOperators/Group/Finset/Basic.lean @@ -178,15 +178,21 @@ theorem prod_subset (h : s₁ ⊆ s₂) (hf : ∀ x ∈ s₂, x ∉ s₁ → f x prod_subset_one_on_sdiff h (by simpa) fun _ _ => rfl @[to_additive (attr := simp)] -theorem prod_disj_sum (s : Finset ι) (t : Finset κ) (f : ι ⊕ κ → M) : +theorem prod_disjSum (s : Finset ι) (t : Finset κ) (f : ι ⊕ κ → M) : ∏ x ∈ s.disjSum t, f x = (∏ x ∈ s, f (Sum.inl x)) * ∏ x ∈ t, f (Sum.inr x) := by rw [← map_inl_disjUnion_map_inr, prod_disjUnion, prod_map, prod_map] rfl +@[deprecated (since := "2025-06-11")] +alias sum_disj_sum := sum_disjSum + +@[to_additive existing, deprecated (since := "2025-06-11")] +alias prod_disj_sum := prod_disjSum + @[to_additive] lemma prod_sum_eq_prod_toLeft_mul_prod_toRight (s : Finset (ι ⊕ κ)) (f : ι ⊕ κ → M) : ∏ x ∈ s, f x = (∏ x ∈ s.toLeft, f (Sum.inl x)) * ∏ x ∈ s.toRight, f (Sum.inr x) := by - rw [← Finset.toLeft_disjSum_toRight (u := s), Finset.prod_disj_sum, Finset.toLeft_disjSum, + rw [← Finset.toLeft_disjSum_toRight (u := s), Finset.prod_disjSum, Finset.toLeft_disjSum, Finset.toRight_disjSum] @[to_additive] diff --git a/Mathlib/Algebra/Category/CoalgCat/ComonEquivalence.lean b/Mathlib/Algebra/Category/CoalgCat/ComonEquivalence.lean index 61c25217c8c466..baa5e42ef97617 100644 --- a/Mathlib/Algebra/Category/CoalgCat/ComonEquivalence.lean +++ b/Mathlib/Algebra/Category/CoalgCat/ComonEquivalence.lean @@ -126,9 +126,10 @@ theorem tensorObj_comul (K L : CoalgCat R) : rw [ofComonObjCoalgebraStruct_comul] dsimp only [Equivalence.symm_inverse, comonEquivalence_functor, toComon_obj] simp only [Comon_.monoidal_tensorObj_comon_comul, Equivalence.symm_inverse, - comonEquivalence_functor, toComon_obj, toComonObj_X, ModuleCat.of_coe, comul_def, - tensorμ_eq_tensorTensorTensorComm, ModuleCat.hom_comp, ModuleCat.hom_ofHom, - LinearEquiv.comp_toLinearMap_eq_iff] + comonEquivalence_functor, toComon_obj, toComonObj_X, ModuleCat.of_coe, + Mon_Class.tensorObj.mul_def, unop_comp, unop_tensorObj, unop_tensorHom, + BraidedCategory.unop_tensorμ, tensorμ_eq_tensorTensorTensorComm, ModuleCat.hom_comp, + ModuleCat.hom_ofHom, LinearEquiv.comp_toLinearMap_eq_iff] rfl theorem tensorHom_toLinearMap (f : M →ₗc[R] N) (g : P →ₗc[R] Q) : @@ -150,6 +151,7 @@ theorem rightUnitor_hom_toLinearMap : open TensorProduct +attribute [local simp] Mon_Class.tensorObj.one_def Mon_Class.tensorObj.mul_def in theorem comul_tensorObj : Coalgebra.comul (R := R) (A := (CoalgCat.of R M ⊗ CoalgCat.of R N : CoalgCat R)) = Coalgebra.comul (A := M ⊗[R] N) := by @@ -158,6 +160,7 @@ theorem comul_tensorObj : AlgebraTensorModule.tensorTensorTensorComm_eq] rfl +attribute [local simp] Mon_Class.tensorObj.one_def Mon_Class.tensorObj.mul_def in theorem comul_tensorObj_tensorObj_right : Coalgebra.comul (R := R) (A := (CoalgCat.of R M ⊗ (CoalgCat.of R N ⊗ CoalgCat.of R P) : CoalgCat R)) @@ -168,6 +171,7 @@ theorem comul_tensorObj_tensorObj_right : AlgebraTensorModule.tensorTensorTensorComm_eq] rfl +attribute [local simp] Mon_Class.tensorObj.one_def Mon_Class.tensorObj.mul_def in theorem comul_tensorObj_tensorObj_left : Coalgebra.comul (R := R) (A := ((CoalgCat.of R M ⊗ CoalgCat.of R N) ⊗ CoalgCat.of R P : CoalgCat R)) diff --git a/Mathlib/Algebra/Category/CommAlgCat/Basic.lean b/Mathlib/Algebra/Category/CommAlgCat/Basic.lean index 349c5e6805ae2c..82e5f553d4123c 100644 --- a/Mathlib/Algebra/Category/CommAlgCat/Basic.lean +++ b/Mathlib/Algebra/Category/CommAlgCat/Basic.lean @@ -158,8 +158,6 @@ def ofIso (i : A ≅ B) : A ≃ₐ[R] B where def isoEquivAlgEquiv : (of R X ≅ of R Y) ≃ (X ≃ₐ[R] Y) where toFun := ofIso invFun := isoMk - left_inv _ := rfl - right_inv _ := rfl instance reflectsIsomorphisms_forget : (forget (CommAlgCat.{u} R)).ReflectsIsomorphisms where reflects {X Y} f _ := by diff --git a/Mathlib/Algebra/Category/Grp/Adjunctions.lean b/Mathlib/Algebra/Category/Grp/Adjunctions.lean index 7a4b6c30907875..f0bf4aef26edf9 100644 --- a/Mathlib/Algebra/Category/Grp/Adjunctions.lean +++ b/Mathlib/Algebra/Category/Grp/Adjunctions.lean @@ -177,9 +177,7 @@ def MonCat.units : MonCat.{u} ⥤ Grp.{u} where def Grp.forget₂MonAdj : forget₂ Grp MonCat ⊣ MonCat.units.{u} := Adjunction.mk' { homEquiv _ Y := { toFun f := ofHom (MonoidHom.toHomUnits f.hom) - invFun f := MonCat.ofHom ((Units.coeHom Y).comp f.hom) - left_inv _ := MonCat.ext fun _ => rfl - right_inv _ := Grp.ext fun _ => Units.ext rfl } + invFun f := MonCat.ofHom ((Units.coeHom Y).comp f.hom) } unit := { app X := ofHom (@toUnits X _) naturality _ _ _ := Grp.ext fun _ => Units.ext rfl } @@ -203,9 +201,7 @@ def CommGrp.forget₂CommMonAdj : forget₂ CommGrp CommMonCat ⊣ CommMonCat.un Adjunction.mk' { homEquiv := fun _ Y ↦ { toFun f := ofHom (MonoidHom.toHomUnits f.hom) - invFun f := CommMonCat.ofHom ((Units.coeHom Y).comp f.hom) - left_inv _ := CommMonCat.ext fun _ => rfl - right_inv _ := CommGrp.ext fun _ => Units.ext rfl } + invFun f := CommMonCat.ofHom ((Units.coeHom Y).comp f.hom) } unit.app X := ofHom toUnits.toMonoidHom -- `aesop` can find the following proof but it takes `0.5`s. unit.naturality _ _ _ := CommGrp.ext fun _ => Units.ext rfl diff --git a/Mathlib/Algebra/Category/ModuleCat/Basic.lean b/Mathlib/Algebra/Category/ModuleCat/Basic.lean index 46983e685ec580..e5f8a19aec9d6e 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Basic.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Basic.lean @@ -183,8 +183,6 @@ lemma hom_inv_apply {M N : ModuleCat.{v} R} (e : M ≅ N) (x : N) : e.hom (e.inv def homEquiv {M N : ModuleCat.{v} R} : (M ⟶ N) ≃ (M →ₗ[R] N) where toFun := Hom.hom invFun := ofHom - left_inv _ := rfl - right_inv _ := rfl end @@ -465,8 +463,6 @@ variable (M N : ModuleCat.{v} R) toFun := ModuleCat.Hom.hom invFun := ModuleCat.ofHom map_mul' _ _ := rfl - left_inv _ := rfl - right_inv _ := rfl map_add' _ _ := rfl /-- `ModuleCat.Hom.hom` as an isomorphism of monoids. -/ diff --git a/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean b/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean index 2dde91a9a5977e..df38e97817ae0e 100644 --- a/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean +++ b/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean @@ -139,8 +139,6 @@ def semilinearMapAddEquiv {R : Type u₁} {S : Type u₂} [Ring R] [Ring S] (f : { toFun := g map_add' := by simp map_smul' := g.hom.map_smul } - left_inv _ := rfl - right_inv _ := rfl map_add' _ _ := rfl section diff --git a/Mathlib/Algebra/Category/ModuleCat/Monoidal/Closed.lean b/Mathlib/Algebra/Category/ModuleCat/Monoidal/Closed.lean index 60179821aac46d..8e120b1d44a04a 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Monoidal/Closed.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Monoidal/Closed.lean @@ -36,7 +36,6 @@ def monoidalClosedHomEquiv (M N P : ModuleCat.{u} R) : simp only [Hom.hom₂_ofHom₂, LinearMap.comp_apply, hom_comp, MonoidalCategory.curriedTensor_obj_obj] erw [MonoidalCategory.braiding_hom_apply m n, TensorProduct.lift.tmul] - right_inv _ := rfl instance : MonoidalClosed (ModuleCat.{u} R) where closed M := diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf/ChangeOfRings.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf/ChangeOfRings.lean index 8a4f34115abb2e..119362781770b8 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Sheaf/ChangeOfRings.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf/ChangeOfRings.lean @@ -62,7 +62,5 @@ noncomputable def restrictHomEquivOfIsLocallySurjective erw [← (g.app _).hom.map_smul] rw [M₁.map_smul, ← hr] rfl) - left_inv _ := rfl - right_inv _ := rfl end PresheafOfModules diff --git a/Mathlib/Algebra/CharP/Defs.lean b/Mathlib/Algebra/CharP/Defs.lean index b32df83cc15a40..c0a95ce4ff29d3 100644 --- a/Mathlib/Algebra/CharP/Defs.lean +++ b/Mathlib/Algebra/CharP/Defs.lean @@ -15,8 +15,8 @@ import Mathlib.Order.Lattice * `CharP R p` expresses that the ring (additive monoid with one) `R` has characteristic `p` * `ringChar`: the characteristic of a ring * `ExpChar R p` expresses that the ring (additive monoid with one) `R` has - exponential characteristic `p` (which is `1` if `R` has characteristic 0, and `p` if it has - prime characteristic `p`) + exponential characteristic `p` (which is `1` if `R` has characteristic 0, and `p` if it has + prime characteristic `p`) -/ assert_not_exists Field Finset OrderHom diff --git a/Mathlib/Algebra/CubicDiscriminant.lean b/Mathlib/Algebra/CubicDiscriminant.lean index 0a27c7c89e2d13..1dddc12e6ef79b 100644 --- a/Mathlib/Algebra/CubicDiscriminant.lean +++ b/Mathlib/Algebra/CubicDiscriminant.lean @@ -19,7 +19,7 @@ This file defines cubic polynomials over a semiring and their discriminants over ## Main statements * `Cubic.disc_ne_zero_iff_roots_nodup`: the cubic discriminant is not equal to zero if and only if - the cubic has no duplicate roots. + the cubic has no duplicate roots. ## References diff --git a/Mathlib/Algebra/DualQuaternion.lean b/Mathlib/Algebra/DualQuaternion.lean index c1178645b683e5..e664c0e4e642c1 100644 --- a/Mathlib/Algebra/DualQuaternion.lean +++ b/Mathlib/Algebra/DualQuaternion.lean @@ -36,8 +36,6 @@ def dualNumberEquiv : Quaternion (DualNumber R) ≃ₐ[R] DualNumber (Quaternion (⟨q.re.fst, q.imI.fst, q.imJ.fst, q.imK.fst⟩, ⟨q.re.snd, q.imI.snd, q.imJ.snd, q.imK.snd⟩) invFun d := ⟨(d.fst.re, d.snd.re), (d.fst.imI, d.snd.imI), (d.fst.imJ, d.snd.imJ), (d.fst.imK, d.snd.imK)⟩ - left_inv := fun _ => rfl - right_inv := fun _ => rfl map_mul' := by intros ext : 1 diff --git a/Mathlib/Algebra/Equiv/TransferInstance.lean b/Mathlib/Algebra/Equiv/TransferInstance.lean index 7f1ea70614d826..2964c8bdf89794 100644 --- a/Mathlib/Algebra/Equiv/TransferInstance.lean +++ b/Mathlib/Algebra/Equiv/TransferInstance.lean @@ -7,6 +7,7 @@ import Mathlib.Algebra.Algebra.Equiv import Mathlib.Algebra.Field.Basic import Mathlib.Logic.Equiv.Defs import Mathlib.Logic.Small.Defs +import Mathlib.Algebra.Ring.Hom.InjSurj /-! # Transfer algebraic structures across `Equiv`s diff --git a/Mathlib/Algebra/Free.lean b/Mathlib/Algebra/Free.lean index 29ad2b03899617..dcf876a623f594 100644 --- a/Mathlib/Algebra/Free.lean +++ b/Mathlib/Algebra/Free.lean @@ -128,8 +128,6 @@ def lift : (α → β) ≃ (FreeMagma α →ₙ* β) where { toFun := liftAux f map_mul' := fun _ _ ↦ rfl } invFun F := F ∘ of - left_inv _ := rfl - right_inv F := by ext; rfl @[to_additive (attr := simp)] theorem lift_of (x) : lift f (of x) = f x := rfl @@ -386,8 +384,6 @@ def lift : (α →ₙ* β) ≃ (AssocQuotient α →ₙ* β) where Quot.liftOn x f <| by rintro a b (⟨c, d, e⟩ | ⟨c, d, e, f⟩) <;> simp only [map_mul, mul_assoc] map_mul' := fun x y ↦ Quot.induction_on₂ x y (map_mul f) } invFun f := f.comp of - left_inv _ := (DFunLike.ext _ _) fun _ ↦ rfl - right_inv _ := hom_ext <| (DFunLike.ext _ _) fun _ ↦ rfl @[to_additive (attr := simp)] theorem lift_of (x : α) : lift f (of x) = f x := rfl @@ -527,8 +523,6 @@ def lift : (α → β) ≃ (FreeSemigroup α →ₙ* β) where simp [head_mul, tail_mul, ← List.foldl_map, List.foldl_append, List.foldl_cons, List.foldl_assoc] } invFun f := f ∘ of - left_inv _ := rfl - right_inv _ := hom_ext rfl @[to_additive (attr := simp)] theorem lift_of (x : α) : lift f (of x) = f x := rfl diff --git a/Mathlib/Algebra/FreeMonoid/Basic.lean b/Mathlib/Algebra/FreeMonoid/Basic.lean index 2f62eebb28fcec..86861ce5dfc72a 100644 --- a/Mathlib/Algebra/FreeMonoid/Basic.lean +++ b/Mathlib/Algebra/FreeMonoid/Basic.lean @@ -307,8 +307,6 @@ def lift : (α → M) ≃ (FreeMonoid α →* M) where map_one' := rfl map_mul' := fun _ _ ↦ by simp only [prodAux_eq, toList_mul, List.map_append, List.prod_append] } invFun f x := f (of x) - left_inv _ := rfl - right_inv _ := hom_eq fun _ ↦ rfl @[to_additive (attr := simp)] theorem lift_ofList (f : α → M) (l : List α) : lift f (ofList l) = (l.map f).prod := diff --git a/Mathlib/Algebra/Group/AddChar.lean b/Mathlib/Algebra/Group/AddChar.lean index 873b801ef3bd18..1e5d85d888528c 100644 --- a/Mathlib/Algebra/Group/AddChar.lean +++ b/Mathlib/Algebra/Group/AddChar.lean @@ -129,8 +129,6 @@ def toMonoidHomEquiv : AddChar A M ≃ (Multiplicative A →* M) where { toFun := f.toFun map_zero_eq_one' := f.map_one' map_add_eq_mul' := f.map_mul' } - left_inv _ := rfl - right_inv _ := rfl @[simp, norm_cast] lemma coe_toMonoidHomEquiv (ψ : AddChar A M) : ⇑(toMonoidHomEquiv ψ) = ψ ∘ Multiplicative.toAdd := rfl @@ -163,8 +161,6 @@ def toAddMonoidHomEquiv : AddChar A M ≃ (A →+ Additive M) where { toFun := f.toFun map_zero_eq_one' := f.map_zero' map_add_eq_mul' := f.map_add' } - left_inv _ := rfl - right_inv _ := rfl @[simp, norm_cast] lemma coe_toAddMonoidHomEquiv (ψ : AddChar A M) : diff --git a/Mathlib/Algebra/Group/End.lean b/Mathlib/Algebra/Group/End.lean index b9f8d6d48e5e7a..53f8a6d0f2c03e 100644 --- a/Mathlib/Algebra/Group/End.lean +++ b/Mathlib/Algebra/Group/End.lean @@ -84,8 +84,6 @@ def equivUnitsEnd : Perm α ≃* Units (Function.End α) where toFun e := ⟨e.toFun, e.symm.toFun, e.self_comp_symm, e.symm_comp_self⟩ invFun u := ⟨(u : Function.End α), (↑u⁻¹ : Function.End α), congr_fun u.inv_val, congr_fun u.val_inv⟩ - left_inv _ := ext fun _ => rfl - right_inv _ := Units.ext rfl map_mul' _ _ := rfl /-- Lift a monoid homomorphism `f : G →* Function.End α` to a monoid homomorphism diff --git a/Mathlib/Algebra/Group/Equiv/Opposite.lean b/Mathlib/Algebra/Group/Equiv/Opposite.lean index 830f21c395ad6b..832db14b7c11ee 100644 --- a/Mathlib/Algebra/Group/Equiv/Opposite.lean +++ b/Mathlib/Algebra/Group/Equiv/Opposite.lean @@ -101,8 +101,6 @@ def MulHom.op {M N} [Mul M] [Mul N] : (M →ₙ* N) ≃ (Mᵐᵒᵖ →ₙ* Nᵐ invFun f := { toFun := unop ∘ f ∘ MulOpposite.op, map_mul' x y := congrArg unop (f.map_mul (MulOpposite.op y) (MulOpposite.op x)) } - left_inv _ := rfl - right_inv _ := rfl /-- The 'unopposite' of a semigroup homomorphism `Mᵐᵒᵖ →ₙ* Nᵐᵒᵖ`. Inverse to `MulHom.op`. -/ @[to_additive (attr := simp) @@ -122,8 +120,6 @@ def AddHom.mulOp {M N} [Add M] [Add N] : AddHom M N ≃ AddHom Mᵐᵒᵖ Nᵐ { toFun := MulOpposite.unop ∘ f ∘ MulOpposite.op, map_add' := fun x y => congrArg MulOpposite.unop (f.map_add (MulOpposite.op x) (MulOpposite.op y)) } - left_inv _ := rfl - right_inv _ := rfl /-- The 'unopposite' of an additive semigroup hom `αᵐᵒᵖ →+ βᵐᵒᵖ`. Inverse to `AddHom.mul_op`. -/ @@ -144,8 +140,6 @@ def MonoidHom.op {M N} [MulOneClass M] [MulOneClass N] : (M →* N) ≃ (Mᵐᵒ invFun f := { toFun := unop ∘ f ∘ MulOpposite.op, map_one' := congrArg unop f.map_one, map_mul' x y := congrArg unop (f.map_mul (MulOpposite.op y) (MulOpposite.op x)) } - left_inv _ := rfl - right_inv _ := rfl /-- The 'unopposite' of a monoid homomorphism `Mᵐᵒᵖ →* Nᵐᵒᵖ`. Inverse to `MonoidHom.op`. -/ @[to_additive (attr := simp) @@ -172,8 +166,6 @@ def AddMonoidHom.mulOp {M N} [AddZeroClass M] [AddZeroClass N] : (M →+ N) ≃ map_zero' := congrArg MulOpposite.unop f.map_zero, map_add' := fun x y => congrArg MulOpposite.unop (f.map_add (MulOpposite.op x) (MulOpposite.op y)) } - left_inv _ := rfl - right_inv _ := rfl /-- The 'unopposite' of an additive monoid hom `αᵐᵒᵖ →+ βᵐᵒᵖ`. Inverse to `AddMonoidHom.mul_op`. -/ @@ -186,8 +178,6 @@ def AddMonoidHom.mulUnop {α β} [AddZeroClass α] [AddZeroClass β] : (αᵐᵒ def AddEquiv.mulOp {α β} [Add α] [Add β] : α ≃+ β ≃ (αᵐᵒᵖ ≃+ βᵐᵒᵖ) where toFun f := opAddEquiv.symm.trans (f.trans opAddEquiv) invFun f := opAddEquiv.trans (f.trans opAddEquiv.symm) - left_inv _ := rfl - right_inv _ := rfl /-- The 'unopposite' of an iso `αᵐᵒᵖ ≃+ βᵐᵒᵖ`. Inverse to `AddEquiv.mul_op`. -/ @[simp] @@ -208,8 +198,6 @@ def MulEquiv.op {α β} [Mul α] [Mul β] : α ≃* β ≃ (αᵐᵒᵖ ≃* β left_inv x := by simp, right_inv x := by simp, map_mul' x y := congr_arg unop (map_mul f (MulOpposite.op y) (MulOpposite.op x)) } - left_inv _ := rfl - right_inv _ := rfl /-- The 'unopposite' of an iso `αᵐᵒᵖ ≃* βᵐᵒᵖ`. Inverse to `MulEquiv.op`. -/ @[to_additive (attr := simp) diff --git a/Mathlib/Algebra/Group/Equiv/TypeTags.lean b/Mathlib/Algebra/Group/Equiv/TypeTags.lean index c143b99f62c6c8..9f30a2eca2b90d 100644 --- a/Mathlib/Algebra/Group/Equiv/TypeTags.lean +++ b/Mathlib/Algebra/Group/Equiv/TypeTags.lean @@ -31,8 +31,6 @@ def AddEquiv.toMultiplicative [AddZeroClass G] [AddZeroClass H] : left_inv := f.left_inv right_inv := f.right_inv map_add' := map_mul f } - left_inv x := by ext; rfl - right_inv x := by ext; rfl /-- Reinterpret `G ≃* H` as `Additive G ≃+ Additive H`. -/ @[simps] @@ -50,8 +48,6 @@ def MulEquiv.toAdditive [MulOneClass G] [MulOneClass H] : left_inv := f.left_inv right_inv := f.right_inv map_mul' := map_add f } - left_inv x := by ext; rfl - right_inv x := by ext; rfl /-- Reinterpret `Additive G ≃+ H` as `G ≃* Multiplicative H`. -/ @[simps] @@ -69,8 +65,6 @@ def AddEquiv.toMultiplicative' [MulOneClass G] [AddZeroClass H] : left_inv := f.left_inv right_inv := f.right_inv map_add' := map_mul f } - left_inv x := by ext; rfl - right_inv x := by ext; rfl /-- Reinterpret `G ≃* Multiplicative H` as `Additive G ≃+ H`. -/ abbrev MulEquiv.toAdditive' [MulOneClass G] [AddZeroClass H] : @@ -93,8 +87,6 @@ def AddEquiv.toMultiplicative'' [AddZeroClass G] [MulOneClass H] : left_inv := f.left_inv right_inv := f.right_inv map_add' := map_mul f } - left_inv x := by ext; rfl - right_inv x := by ext; rfl /-- Reinterpret `Multiplicative G ≃* H` as `G ≃+ Additive H` as. -/ abbrev MulEquiv.toAdditive'' [AddZeroClass G] [MulOneClass H] : @@ -133,8 +125,6 @@ def MulEquiv.piMultiplicative (K : ι → Type*) [∀ i, Add (K i)] : Multiplicative (∀ i : ι, K i) ≃* (∀ i : ι, Multiplicative (K i)) where toFun x := fun i ↦ Multiplicative.ofAdd <| x.toAdd i invFun x := Multiplicative.ofAdd fun i ↦ (x i).toAdd - left_inv _ := rfl - right_inv _ := rfl map_mul' _ _ := rfl variable (ι) (G) in @@ -149,8 +139,6 @@ def AddEquiv.piAdditive (K : ι → Type*) [∀ i, Mul (K i)] : Additive (∀ i : ι, K i) ≃+ (∀ i : ι, Additive (K i)) where toFun x := fun i ↦ Additive.ofMul <| x.toMul i invFun x := Additive.ofMul fun i ↦ (x i).toMul - left_inv _ := rfl - right_inv _ := rfl map_add' _ _ := rfl variable (ι) (G) in @@ -180,8 +168,6 @@ def MulEquiv.prodMultiplicative [Add G] [Add H] : toFun x := (Multiplicative.ofAdd x.toAdd.1, Multiplicative.ofAdd x.toAdd.2) invFun := fun (x, y) ↦ Multiplicative.ofAdd (x.toAdd, y.toAdd) - left_inv _ := rfl - right_inv _ := rfl map_mul' _ _ := rfl /-- `Additive (G × H)` is equivalent to `Additive G × Additive H`. -/ @@ -191,8 +177,6 @@ def AddEquiv.prodAdditive [Mul G] [Mul H] : toFun x := (Additive.ofMul x.toMul.1, Additive.ofMul x.toMul.2) invFun := fun (x, y) ↦ Additive.ofMul (x.toMul, y.toMul) - left_inv _ := rfl - right_inv _ := rfl map_add' _ _ := rfl end diff --git a/Mathlib/Algebra/Group/Even.lean b/Mathlib/Algebra/Group/Even.lean index 7c77822ac64f97..130537500e2648 100644 --- a/Mathlib/Algebra/Group/Even.lean +++ b/Mathlib/Algebra/Group/Even.lean @@ -6,6 +6,7 @@ Authors: Damiano Testa import Mathlib.Algebra.Group.Equiv.Basic import Mathlib.Algebra.Group.Equiv.Opposite import Mathlib.Algebra.Group.TypeTags.Basic +import Mathlib.Data.Set.Operations /-! # Squares and even elements @@ -51,6 +52,11 @@ for some root `r : α`. -/ for some `r : α`."] def IsSquare (a : α) : Prop := ∃ r, a = r * r +@[to_additive] +lemma isSquare_iff_exists_mul_self (a : α) : IsSquare a ↔ ∃ r, a = r * r := .rfl + +@[to_additive] alias ⟨IsSquare.exists_mul_self, _⟩ := isSquare_iff_exists_mul_self + @[to_additive (attr := simp)] lemma IsSquare.mul_self (r : α) : IsSquare (r * r) := ⟨r, rfl⟩ @[to_additive] @@ -101,7 +107,13 @@ variable [MulOneClass α] [MulOneClass β] [FunLike F α β] [MonoidHomClass F @[to_additive] lemma IsSquare.map {a : α} (f : F) : IsSquare a → IsSquare (f a) := - fun ⟨r, _⟩ => ⟨f r, by simp_all⟩ + fun ⟨r, _⟩ => ⟨f r, by simp [*]⟩ + +@[to_additive] +lemma isSquare_subset_image_isSquare {f : F} (hf : Function.Surjective f) : + {b | IsSquare b} ⊆ f '' {a | IsSquare a} := fun b ⟨s, _⟩ => by + rcases hf s with ⟨r, rfl⟩ + exact ⟨r * r, by simp [*]⟩ end MonoidHom diff --git a/Mathlib/Algebra/Group/Irreducible/Defs.lean b/Mathlib/Algebra/Group/Irreducible/Defs.lean index b508ece672c68c..c465957cbfd439 100644 --- a/Mathlib/Algebra/Group/Irreducible/Defs.lean +++ b/Mathlib/Algebra/Group/Irreducible/Defs.lean @@ -30,7 +30,7 @@ structure AddIrreducible [AddMonoid M] (p : M) : Prop where isAddUnit_or_isAddUnit ⦃a b⦄ : p = a + b → IsAddUnit a ∨ IsAddUnit b section Monoid -variable [Monoid M] {p q x y : M} +variable [Monoid M] {p q a b : M} /-- `Irreducible p` states that `p` is non-unit and only factors into units. @@ -41,7 +41,7 @@ structure Irreducible (p : M) : Prop where /-- An irreducible element is not a unit. -/ not_isUnit : ¬IsUnit p /-- If an irreducible element factors, then one factor is a unit. -/ - isUnit_or_isUnit ⦃a b⦄ : p = a * b → IsUnit a ∨ IsUnit b + isUnit_or_isUnit ⦃a b : M⦄ : p = a * b → IsUnit a ∨ IsUnit b namespace Irreducible @@ -62,11 +62,15 @@ lemma not_irreducible_one : ¬Irreducible (1 : M) := by simp [irreducible_iff] lemma Irreducible.ne_one (hp : Irreducible p) : p ≠ 1 := by rintro rfl; exact not_irreducible_one hp @[to_additive] -lemma of_irreducible_mul : Irreducible (x * y) → IsUnit x ∨ IsUnit y | ⟨_, h⟩ => h rfl +lemma of_irreducible_mul : Irreducible (a * b) → IsUnit a ∨ IsUnit b | ⟨_, h⟩ => h rfl @[to_additive] lemma irreducible_or_factor (hp : ¬IsUnit p) : Irreducible p ∨ ∃ a b, ¬IsUnit a ∧ ¬IsUnit b ∧ p = a * b := by simpa [irreducible_iff, hp, and_rotate] using em (∀ a b, p = a * b → IsUnit a ∨ IsUnit b) +@[to_additive] +lemma Irreducible.eq_one_or_eq_one [Subsingleton Mˣ] (hab : Irreducible (a * b)) : + a = 1 ∨ b = 1 := by simpa using hab.isUnit_or_isUnit rfl + end Monoid diff --git a/Mathlib/Algebra/Group/Pi/Basic.lean b/Mathlib/Algebra/Group/Pi/Basic.lean index dd8dda8b24eba0..d098a1fc1a6dee 100644 --- a/Mathlib/Algebra/Group/Pi/Basic.lean +++ b/Mathlib/Algebra/Group/Pi/Basic.lean @@ -201,8 +201,6 @@ theorem mulSingle_op₂ (op : ∀ i, M i → N i → O i) (h : ∀ i, op i 1 1 = mulSingle i (op i x₁ x₂) = fun j => op j (mulSingle i x₁ j) (mulSingle i x₂ j) := Eq.symm <| funext <| apply_mulSingle₂ op h i x₁ x₂ -variable (f) - @[to_additive] theorem mulSingle_injective (i : ι) : Function.Injective (mulSingle i : M i → ∀ i, M i) := Function.update_injective _ i diff --git a/Mathlib/Algebra/Group/Pi/Lemmas.lean b/Mathlib/Algebra/Group/Pi/Lemmas.lean index efb883d26cb0f5..dd3d8289e255a6 100644 --- a/Mathlib/Algebra/Group/Pi/Lemmas.lean +++ b/Mathlib/Algebra/Group/Pi/Lemmas.lean @@ -187,8 +187,7 @@ variable [DecidableEq I] open Pi -variable (f) - +variable (f) in /-- The one-preserving homomorphism including a single value into a dependent family of values, as functions supported at a point. @@ -206,6 +205,11 @@ nonrec def OneHom.mulSingle [∀ i, One <| f i] (i : I) : OneHom (f i) (∀ i, f theorem OneHom.mulSingle_apply [∀ i, One <| f i] (i : I) (x : f i) : mulSingle f i x = Pi.mulSingle i x := rfl +@[to_additive (attr := simp, norm_cast)] +theorem OneHom.coe_mulSingle [∀ i, One <| f i] (i : I) : + mulSingle f i = Pi.mulSingle (M := f) i := rfl + +variable (f) in /-- The monoid homomorphism including a single monoid into a dependent family of additive monoids, as functions supported at a point. @@ -223,7 +227,9 @@ theorem MonoidHom.mulSingle_apply [∀ i, MulOneClass <| f i] (i : I) (x : f i) mulSingle f i x = Pi.mulSingle i x := rfl -variable {f} +@[to_additive (attr := simp, norm_cast)] +theorem MonoidHom.coe_mulSingle [∀ i, MulOneClass <| f i] (i : I) : + mulSingle f i = Pi.mulSingle (M := f) i := rfl @[to_additive] theorem Pi.mulSingle_sup [∀ i, SemilatticeSup (f i)] [∀ i, One (f i)] (i : I) (x y : f i) : diff --git a/Mathlib/Algebra/Group/Pi/Units.lean b/Mathlib/Algebra/Group/Pi/Units.lean index ba43080762225f..97a0bb29cb3ad1 100644 --- a/Mathlib/Algebra/Group/Pi/Units.lean +++ b/Mathlib/Algebra/Group/Pi/Units.lean @@ -21,8 +21,6 @@ and the product of the units of each monoid. -/ def MulEquiv.piUnits : (Π i, M i)ˣ ≃* Π i, (M i)ˣ where toFun f i := ⟨f.val i, f.inv i, congr_fun f.val_inv i, congr_fun f.inv_val i⟩ invFun f := ⟨(val <| f ·), (inv <| f ·), funext (val_inv <| f ·), funext (inv_val <| f ·)⟩ - left_inv _ := rfl - right_inv _ := rfl map_mul' _ _ := rfl @[to_additive] diff --git a/Mathlib/Algebra/Group/Subgroup/Ker.lean b/Mathlib/Algebra/Group/Subgroup/Ker.lean index 116e4492ab0b8e..4203b8411a59ba 100644 --- a/Mathlib/Algebra/Group/Subgroup/Ker.lean +++ b/Mathlib/Algebra/Group/Subgroup/Ker.lean @@ -471,6 +471,22 @@ theorem map_subtype_le_map_subtype {G' : Subgroup G} {H K : Subgroup G'} : H.map G'.subtype ≤ K.map G'.subtype ↔ H ≤ K := map_le_map_iff_of_injective G'.subtype_injective +/-- Subgroups of the subgroup `H` are considered as subgroups that are less than or equal to +`H`. -/ +@[to_additive (attr := simps apply_coe) "Additive subgroups of the subgroup `H` are considered as +additive subgroups that are less than or equal to `H`."] +def MapSubtype.orderIso (H : Subgroup G) : Subgroup ↥H ≃o { H' : Subgroup G // H' ≤ H } where + toFun H' := ⟨H'.map H.subtype, map_subtype_le H'⟩ + invFun sH' := sH'.1.subgroupOf H + left_inv H' := comap_map_eq_self_of_injective H.subtype_injective H' + right_inv sH' := Subtype.ext (map_subgroupOf_eq_of_le sH'.2) + map_rel_iff' := by simp + +@[to_additive (attr := simp)] +lemma MapSubtype.orderIso_symm_apply (H : Subgroup G) (sH' : { H' : Subgroup G // H' ≤ H }) : + (MapSubtype.orderIso H).symm sH' = sH'.1.subgroupOf H := + rfl + @[to_additive] theorem map_lt_map_iff_of_injective {f : G →* N} (hf : Function.Injective f) {H K : Subgroup G} : H.map f < K.map f ↔ H < K := diff --git a/Mathlib/Algebra/Group/Subgroup/Map.lean b/Mathlib/Algebra/Group/Subgroup/Map.lean index dfe68db9829c17..2a32e1926254bb 100644 --- a/Mathlib/Algebra/Group/Subgroup/Map.lean +++ b/Mathlib/Algebra/Group/Subgroup/Map.lean @@ -280,8 +280,6 @@ def subgroupOfEquivOfLe {G : Type*} [Group G] {H K : Subgroup G} (h : H ≤ K) : H.subgroupOf K ≃* H where toFun g := ⟨g.1, g.2⟩ invFun g := ⟨⟨g.1, h g.2⟩, g.2⟩ - left_inv _g := Subtype.ext (Subtype.ext rfl) - right_inv _g := Subtype.ext rfl map_mul' _g _h := rfl @[to_additive (attr := simp)] diff --git a/Mathlib/Algebra/Group/Submonoid/Operations.lean b/Mathlib/Algebra/Group/Submonoid/Operations.lean index 80aae71eb7463e..2cd00eb74cbebd 100644 --- a/Mathlib/Algebra/Group/Submonoid/Operations.lean +++ b/Mathlib/Algebra/Group/Submonoid/Operations.lean @@ -429,7 +429,6 @@ def topEquiv : (⊤ : Submonoid M) ≃* M where toFun x := x invFun x := ⟨x, mem_top x⟩ left_inv x := x.eta _ - right_inv _ := rfl map_mul' _ _ := rfl @[to_additive (attr := simp)] diff --git a/Mathlib/Algebra/Group/Submonoid/Units.lean b/Mathlib/Algebra/Group/Submonoid/Units.lean index c38ff4ff41d584..5616090a3ef10b 100644 --- a/Mathlib/Algebra/Group/Submonoid/Units.lean +++ b/Mathlib/Algebra/Group/Submonoid/Units.lean @@ -135,8 +135,6 @@ lemma inv_mem_units_iff (S : Submonoid M) {x : Mˣ} : x⁻¹ ∈ S.units ↔ x def unitsEquivUnitsType (S : Submonoid M) : S.units ≃* Sˣ where toFun := fun ⟨_, h⟩ => ⟨⟨_, h.1⟩, ⟨_, h.2⟩, S.mk_mul_mk_inv_eq_one h, S.mk_inv_mul_mk_eq_one h⟩ invFun := fun x => ⟨⟨_, _, S.coe_val_mul_coe_inv_val, S.coe_inv_val_mul_coe_val⟩, ⟨x.1.2, x.2.2⟩⟩ - left_inv := fun _ => rfl - right_inv := fun _ => rfl map_mul' := fun _ _ => rfl @[to_additive (attr := simp)] @@ -251,8 +249,6 @@ the subgroup itself as a type. -/ noncomputable def ofUnitsEquivType (S : Subgroup Mˣ) : S.ofUnits ≃* S where toFun := fun x => ⟨S.unit_of_mem_ofUnits x.2, S.unit_of_mem_ofUnits_spec_mem⟩ invFun := fun x => ⟨x.1, ⟨x.1, x.2, rfl⟩⟩ - left_inv := fun _ => rfl - right_inv := fun _ => Subtype.ext (Units.ext rfl) map_mul' := fun _ _ => Subtype.ext (Units.ext rfl) @[to_additive (attr := simp)] diff --git a/Mathlib/Algebra/Group/Subsemigroup/Operations.lean b/Mathlib/Algebra/Group/Subsemigroup/Operations.lean index 01690fee987de4..1334c50299d911 100644 --- a/Mathlib/Algebra/Group/Subsemigroup/Operations.lean +++ b/Mathlib/Algebra/Group/Subsemigroup/Operations.lean @@ -84,8 +84,6 @@ def Subsemigroup.toAddSubsemigroup : Subsemigroup M ≃o AddSubsemigroup (Additi invFun S := { carrier := Additive.ofMul ⁻¹' S mul_mem' := S.add_mem' } - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := Iff.rfl /-- Additive subsemigroups of an additive semigroup `Additive M` are isomorphic to subsemigroups @@ -125,8 +123,6 @@ def AddSubsemigroup.toSubsemigroup : AddSubsemigroup A ≃o Subsemigroup (Multip invFun S := { carrier := Multiplicative.ofAdd ⁻¹' S add_mem' := S.mul_mem' } - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := Iff.rfl /-- Subsemigroups of a semigroup `Multiplicative A` are isomorphic to additive subsemigroups @@ -429,7 +425,6 @@ def topEquiv : (⊤ : Subsemigroup M) ≃* M where toFun x := x invFun x := ⟨x, mem_top x⟩ left_inv x := x.eta _ - right_inv _ := rfl map_mul' _ _ := rfl @[to_additive (attr := simp)] diff --git a/Mathlib/Algebra/Group/TypeTags/Hom.lean b/Mathlib/Algebra/Group/TypeTags/Hom.lean index 78795b04fc9384..6a4fdb882cd0ec 100644 --- a/Mathlib/Algebra/Group/TypeTags/Hom.lean +++ b/Mathlib/Algebra/Group/TypeTags/Hom.lean @@ -32,8 +32,6 @@ def AddMonoidHom.toMultiplicative [AddZeroClass α] [AddZeroClass β] : map_add' := f.map_mul map_zero' := f.map_one } - left_inv _ := rfl - right_inv _ := rfl @[simp, norm_cast] lemma AddMonoidHom.coe_toMultiplicative [AddZeroClass α] [AddZeroClass β] (f : α →+ β) : @@ -53,8 +51,6 @@ def MonoidHom.toAdditive [MulOneClass α] [MulOneClass β] : map_mul' := f.map_add map_one' := f.map_zero } - left_inv _ := rfl - right_inv _ := rfl @[simp, norm_cast] lemma MonoidHom.coe_toMultiplicative [MulOneClass α] [MulOneClass β] (f : α →* β) : @@ -74,8 +70,6 @@ def AddMonoidHom.toMultiplicative' [MulOneClass α] [AddZeroClass β] : map_add' := f.map_mul map_zero' := f.map_one } - left_inv _ := rfl - right_inv _ := rfl @[simp, norm_cast] lemma AddMonoidHom.coe_toMultiplicative' [MulOneClass α] [AddZeroClass β] (f : Additive α →+ β) : @@ -105,8 +99,6 @@ def AddMonoidHom.toMultiplicative'' [AddZeroClass α] [MulOneClass β] : map_add' := f.map_mul map_zero' := f.map_one } - left_inv _ := rfl - right_inv _ := rfl @[simp, norm_cast] lemma AddMonoidHom.coe_toMultiplicative'' [AddZeroClass α] [MulOneClass β] (f : α →+ Additive β) : diff --git a/Mathlib/Algebra/Group/Units/Equiv.lean b/Mathlib/Algebra/Group/Units/Equiv.lean index 60f5a8f7a0ef06..bbb87f6aecb725 100644 --- a/Mathlib/Algebra/Group/Units/Equiv.lean +++ b/Mathlib/Algebra/Group/Units/Equiv.lean @@ -20,8 +20,6 @@ variable {F α M N G : Type*} def toUnits [Group G] : G ≃* Gˣ where toFun x := ⟨x, x⁻¹, mul_inv_cancel _, inv_mul_cancel _⟩ invFun x := x - left_inv _ := rfl - right_inv _ := Units.ext rfl map_mul' _ _ := Units.ext rfl @[to_additive (attr := simp)] @@ -169,8 +167,6 @@ variable (α) in def unitsEquivProdSubtype [Monoid α] : αˣ ≃ {p : α × α // p.1 * p.2 = 1 ∧ p.2 * p.1 = 1} where toFun u := ⟨(u, ↑u⁻¹), u.val_inv, u.inv_val⟩ invFun p := Units.mk (p : α × α).1 (p : α × α).2 p.prop.1 p.prop.2 - left_inv _ := Units.ext rfl - right_inv _ := Subtype.ext <| Prod.ext rfl rfl /-- In a `DivisionCommMonoid`, `Equiv.inv` is a `MulEquiv`. There is a variant of this `MulEquiv.inv' G : G ≃* Gᵐᵒᵖ` for the non-commutative case. -/ diff --git a/Mathlib/Algebra/Group/Units/Hom.lean b/Mathlib/Algebra/Group/Units/Hom.lean index eb2ff825c9065f..204e5a7befb9fc 100644 --- a/Mathlib/Algebra/Group/Units/Hom.lean +++ b/Mathlib/Algebra/Group/Units/Hom.lean @@ -17,10 +17,12 @@ also contains unrelated results about `Units` that depend on `MonoidHom`. * `Units.map`: Turn a homomorphism from `α` to `β` monoids into a homomorphism from `αˣ` to `βˣ`. * `MonoidHom.toHomUnits`: Turn a homomorphism from a group `α` to `β` into a homomorphism from `α` to `βˣ`. -* `IsLocalHom`: A predicate on monoid maps, requiring that it maps nonunits - to nonunits. For local rings, this means that the image of the unique maximal ideal is again - contained in the unique maximal ideal. This is developed earlier, and in the generality of - monoids, as it allows its use in non-local-ring related contexts, but it does have the +* `IsLocalHom`: A predicate on monoid maps, requiring that it maps + nonunits to nonunits. For the local rings, that is, applied to their + multiplicative monoids, this means that the image of the unique + maximal ideal is again contained in the unique maximal ideal. This + is developed earlier, and in the generality of monoids, as it allows + its use in non-local-ring related contexts, but it does have the strange consequence that it does not require local rings, or even rings. ## TODO @@ -218,8 +220,8 @@ variable {G R S T F : Type*} variable [Monoid R] [Monoid S] [Monoid T] [FunLike F R S] -/-- A local ring homomorphism is a map `f` between monoids such that `a` in the domain - is a unit if `f a` is a unit for any `a`. See `IsLocalRing.local_hom_TFAE` for other equivalent +/-- A map `f` between monoids is *local* if any `a` in the domain is a unit + whenever `f a` is a unit. See `IsLocalRing.local_hom_TFAE` for other equivalent definitions in the local ring case - from where this concept originates, but it is useful in other contexts, so we allow this generalisation in mathlib. -/ class IsLocalHom (f : F) : Prop where diff --git a/Mathlib/Algebra/Group/Units/Opposite.lean b/Mathlib/Algebra/Group/Units/Opposite.lean index 24dc26aa2d6cfe..a1b1fe84ef6556 100644 --- a/Mathlib/Algebra/Group/Units/Opposite.lean +++ b/Mathlib/Algebra/Group/Units/Opposite.lean @@ -25,8 +25,6 @@ def Units.opEquiv {M} [Monoid M] : Mᵐᵒᵖˣ ≃* Mˣᵐᵒᵖ where toFun u := op ⟨unop u, unop ↑u⁻¹, op_injective u.4, op_injective u.3⟩ invFun := MulOpposite.rec' fun u => ⟨op ↑u, op ↑u⁻¹, unop_injective <| u.4, unop_injective u.3⟩ map_mul' _ _ := unop_injective <| Units.ext <| rfl - left_inv x := Units.ext <| by simp - right_inv x := unop_injective <| Units.ext <| by rfl @[to_additive (attr := simp)] theorem Units.coe_unop_opEquiv {M} [Monoid M] (u : Mᵐᵒᵖˣ) : diff --git a/Mathlib/Algebra/Group/WithOne/Basic.lean b/Mathlib/Algebra/Group/WithOne/Basic.lean index 01247252a042f5..90f44aebdeda75 100644 --- a/Mathlib/Algebra/Group/WithOne/Basic.lean +++ b/Mathlib/Algebra/Group/WithOne/Basic.lean @@ -54,7 +54,6 @@ def lift : (α →ₙ* β) ≃ (WithOne α →* β) where (fun x => WithOne.cases_on y (by rw [mul_one]; exact (mul_one _).symm) (fun y => f.map_mul x y)) } invFun F := F.toMulHom.comp coeMulHom - left_inv _ := MulHom.ext fun _ => rfl right_inv F := MonoidHom.ext fun x => WithOne.cases_on x F.map_one.symm (fun _ => rfl) variable (f : α →ₙ* β) diff --git a/Mathlib/Algebra/Group/WithOne/Defs.lean b/Mathlib/Algebra/Group/WithOne/Defs.lean index 937a34af09a6da..5657abb4878b95 100644 --- a/Mathlib/Algebra/Group/WithOne/Defs.lean +++ b/Mathlib/Algebra/Group/WithOne/Defs.lean @@ -89,6 +89,12 @@ def coe : α → WithOne α := instance instCoeTC : CoeTC α (WithOne α) := ⟨coe⟩ +@[to_additive] +lemma «forall» {p : WithZero α → Prop} : (∀ x, p x) ↔ p 0 ∧ ∀ a : α, p a := Option.forall + +@[to_additive] +lemma «exists» {p : WithZero α → Prop} : (∃ x, p x) ↔ p 0 ∨ ∃ a : α, p a := Option.exists + /-- Recursor for `WithZero` using the preferred forms `0` and `↑a`. -/ @[elab_as_elim, induction_eliminator, cases_eliminator] def _root_.WithZero.recZeroCoe {motive : WithZero α → Sort*} (zero : motive 0) diff --git a/Mathlib/Algebra/GroupWithZero/Action/Center.lean b/Mathlib/Algebra/GroupWithZero/Action/Center.lean index 9f779ac4c64892..28999262b4e9c6 100644 --- a/Mathlib/Algebra/GroupWithZero/Action/Center.lean +++ b/Mathlib/Algebra/GroupWithZero/Action/Center.lean @@ -25,6 +25,4 @@ def Subgroup.centerUnitsEquivUnitsCenter (G₀ : Type*) [GroupWithZero G₀] : map_one' := rfl map_mul' _ _ := rfl } invFun u := unitsCenterToCenterUnits G₀ u - left_inv _ := by ext; rfl - right_inv _ := by ext; rfl map_mul' := map_mul _ diff --git a/Mathlib/Algebra/GroupWithZero/Hom.lean b/Mathlib/Algebra/GroupWithZero/Hom.lean index bf55a776230ee3..7f277403c30e61 100644 --- a/Mathlib/Algebra/GroupWithZero/Hom.lean +++ b/Mathlib/Algebra/GroupWithZero/Hom.lean @@ -214,6 +214,26 @@ instance {β} [CommMonoidWithZero β] : Mul (α →*₀ β) where { (f * g : α →* β) with map_zero' := by dsimp; rw [map_zero, zero_mul] } +/-- The trivial homomorphism between monoids with zero, sending everything to 1 other than 0. -/ +protected instance one (M₀ N₀ : Type*) [MulZeroOneClass M₀] [MulZeroOneClass N₀] + [DecidablePred fun x : M₀ ↦ x = 0] [Nontrivial M₀] [NoZeroDivisors M₀] : + One (M₀ →*₀ N₀) where + one.toFun x := if x = 0 then 0 else 1 + one.map_zero' := by simp + one.map_one' := by simp + one.map_mul' x y := by split_ifs <;> simp_all + +@[simp] +lemma one_apply_zero {M₀ N₀ : Type*} [MulZeroOneClass M₀] [MulZeroOneClass N₀] + [DecidablePred fun x : M₀ ↦ x = 0] [Nontrivial M₀] [NoZeroDivisors M₀] : + (1 : M₀ →*₀ N₀) 0 = 0 := + if_pos rfl + +lemma one_apply_of_ne_zero {M₀ N₀ : Type*} [MulZeroOneClass M₀] [MulZeroOneClass N₀] + [DecidablePred fun x : M₀ ↦ x = 0] [Nontrivial M₀] [NoZeroDivisors M₀] {x : M₀} (hx : x ≠ 0) : + (1 : M₀ →*₀ N₀) x = 1 := + if_neg hx + end MonoidWithZeroHom section CommMonoidWithZero diff --git a/Mathlib/Algebra/GroupWithZero/NonZeroDivisors.lean b/Mathlib/Algebra/GroupWithZero/NonZeroDivisors.lean index b6b496ec9487b8..4597c2cfcc8a39 100644 --- a/Mathlib/Algebra/GroupWithZero/NonZeroDivisors.lean +++ b/Mathlib/Algebra/GroupWithZero/NonZeroDivisors.lean @@ -281,7 +281,6 @@ variable {G₀ : Type*} [GroupWithZero G₀] {x : G₀} noncomputable def nonZeroDivisorsEquivUnits : G₀⁰ ≃* G₀ˣ where toFun u := .mk0 _ <| mem_nonZeroDivisors_iff_ne_zero.1 u.2 invFun u := ⟨u, u.isUnit.mem_nonZeroDivisors⟩ - left_inv u := rfl right_inv u := by simp map_mul' u v := by simp @@ -325,8 +324,6 @@ def unitsNonZeroDivisorsEquiv : M₀⁰ˣ ≃* M₀ˣ where __ := Units.map M₀⁰.subtype invFun u := ⟨⟨u, u.isUnit.mem_nonZeroDivisors⟩, ⟨(u⁻¹ : M₀ˣ), u⁻¹.isUnit.mem_nonZeroDivisors⟩, by simp, by simp⟩ - left_inv _ := rfl - right_inv _ := rfl @[simp, norm_cast] lemma nonZeroDivisors.associated_coe : Associated (a : M₀) b ↔ Associated a b := unitsNonZeroDivisorsEquiv.symm.exists_congr_left.trans <| by simp [Associated]; norm_cast diff --git a/Mathlib/Algebra/GroupWithZero/Prod.lean b/Mathlib/Algebra/GroupWithZero/Prod.lean index 97fcd9eafb9a0c..80a4fcbcba2d11 100644 --- a/Mathlib/Algebra/GroupWithZero/Prod.lean +++ b/Mathlib/Algebra/GroupWithZero/Prod.lean @@ -6,6 +6,7 @@ Authors: Eric Wieser, Yaël Dillies import Mathlib.Algebra.Group.Prod import Mathlib.Algebra.GroupWithZero.Hom import Mathlib.Algebra.GroupWithZero.Units.Basic +import Mathlib.Algebra.GroupWithZero.WithZero /-! # Products of monoids with zero, groups with zero @@ -49,6 +50,14 @@ instance instCommMonoidWithZero [CommMonoidWithZero M₀] [CommMonoidWithZero N end Prod +variable (M₀) in +@[simp] +lemma WithZero.toMonoidWithZeroHom_withZeroUnitsEquiv [GroupWithZero M₀] + [DecidablePred fun x : M₀ ↦ x = 0] : + MonoidWithZeroHomClass.toMonoidWithZeroHom WithZero.withZeroUnitsEquiv = + WithZero.lift' (Units.coeHom M₀) := + rfl + /-! ### Multiplication and division as homomorphisms -/ section BundledMulDiv diff --git a/Mathlib/Algebra/GroupWithZero/ProdHom.lean b/Mathlib/Algebra/GroupWithZero/ProdHom.lean new file mode 100644 index 00000000000000..2278c65f80b054 --- /dev/null +++ b/Mathlib/Algebra/GroupWithZero/ProdHom.lean @@ -0,0 +1,163 @@ +/- +Copyright (c) 2025 Yakov Pechersky. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Yakov Pechersky +-/ +import Mathlib.Algebra.Group.Prod +import Mathlib.Algebra.GroupWithZero.Commute +import Mathlib.Algebra.GroupWithZero.Units.Lemmas +import Mathlib.Algebra.GroupWithZero.WithZero + +/-! +# Homomorphisms for products of groups with zero + +This file defines homomorphisms for products of groups with zero, +which is identified with the `WithZero` of the product of the units of the groups. + +The product of groups with zero `WithZero (αˣ × βˣ)` is a +group with zero itself with natural inclusions. + +TODO: Give `GrpWithZero` instances of `HasBinaryProducts` and `HasBinaryCoproducts`, +as well as a terminal object. + +-/ + +namespace MonoidWithZeroHom + +/-- The trivial group-with-zero hom is absorbing for composition. -/ +@[simp] +lemma one_apply_apply_eq {M₀ N₀ G₀ : Type*} + [GroupWithZero M₀] + [MulZeroOneClass N₀] [Nontrivial N₀] [NoZeroDivisors N₀] + [MulZeroOneClass G₀] + [DecidablePred fun x : M₀ ↦ x = 0] [DecidablePred fun x : N₀ ↦ x = 0] + (f : M₀ →*₀ N₀) (x : M₀) : + (1 : N₀ →*₀ G₀) (f x) = (1 : M₀ →*₀ G₀) x := by + rcases eq_or_ne x 0 with rfl | hx + · simp + · rw [one_apply_of_ne_zero hx, one_apply_of_ne_zero] + rwa [map_ne_zero f] + +/-- The trivial group-with-zero hom is absorbing for composition. -/ +@[simp] +lemma one_comp {M₀ N₀ G₀ : Type*} + [GroupWithZero M₀] + [MulZeroOneClass N₀] [Nontrivial N₀] [NoZeroDivisors N₀] + [MulZeroOneClass G₀] + [DecidablePred fun x : M₀ ↦ x = 0] [DecidablePred fun x : N₀ ↦ x = 0] + (f : M₀ →*₀ N₀) : + (1 : N₀ →*₀ G₀).comp f = (1 : M₀ →*₀ G₀) := + ext <| one_apply_apply_eq _ + +variable (G₀ H₀ : Type*) [GroupWithZero G₀] [GroupWithZero H₀] + +/-- Given groups with zero `G₀`, `H₀`, the natural inclusion ordered homomorphism from +`G₀` to `WithZero (G₀ˣ × H₀ˣ)`, which is the group with zero that can be identified +as their product. -/ +def inl [DecidablePred fun x : G₀ ↦ x = 0] : G₀ →*₀ WithZero (G₀ˣ × H₀ˣ) := + (WithZero.map' (.inl _ _)).comp + (MonoidWithZeroHomClass.toMonoidWithZeroHom WithZero.withZeroUnitsEquiv.symm) + +/-- Given groups with zero `G₀`, `H₀`, the natural inclusion ordered homomorphism from +`H₀` to `WithZero (G₀ˣ × H₀ˣ)`, which is the group with zero that can be identified +as their product. -/ +def inr [DecidablePred fun x : H₀ ↦ x = 0] : H₀ →*₀ WithZero (G₀ˣ × H₀ˣ) := + (WithZero.map' (.inr _ _)).comp + (MonoidWithZeroHomClass.toMonoidWithZeroHom WithZero.withZeroUnitsEquiv.symm) + +/-- Given groups with zero `G₀`, `H₀`, the natural projection homomorphism from +`WithZero (G₀ˣ × H₀ˣ)` to `G₀`, which is the group with zero that can be identified +as their product. -/ +def fst : WithZero (G₀ˣ × H₀ˣ) →*₀ G₀ := + WithZero.lift' ((Units.coeHom _).comp (.fst ..)) + +/-- Given groups with zero `G₀`, `H₀`, the natural projection homomorphism from +`WithZero (G₀ˣ × H₀ˣ)` to `H₀`, which is the group with zero that can be identified +as their product. -/ +def snd : WithZero (G₀ˣ × H₀ˣ) →*₀ H₀ := + WithZero.lift' ((Units.coeHom _).comp (.snd ..)) + +variable {G₀ H₀} + +@[simp] +lemma inl_apply_unit [DecidablePred fun x : G₀ ↦ x = 0] (x : G₀ˣ) : + inl G₀ H₀ x = ((x, (1 : H₀ˣ)) : WithZero (G₀ˣ × H₀ˣ)) := by + simp [inl] + +@[simp] +lemma inr_apply_unit [DecidablePred fun x : H₀ ↦ x = 0] (x : H₀ˣ) : + inr G₀ H₀ x = (((1 : G₀ˣ), x) : WithZero (G₀ˣ × H₀ˣ)) := by + simp [inr] + +@[simp] lemma fst_apply_coe (x : G₀ˣ × H₀ˣ) : fst G₀ H₀ x = x.fst := by rfl +@[simp] lemma snd_apply_coe (x : G₀ˣ × H₀ˣ) : snd G₀ H₀ x = x.snd := by rfl + +@[simp] +theorem fst_inl [DecidablePred fun x : G₀ ↦ x = 0] (x : G₀) : + fst _ H₀ (inl _ _ x) = x := by + obtain rfl | ⟨_, rfl⟩ := GroupWithZero.eq_zero_or_unit x <;> + simp [WithZero.withZeroUnitsEquiv, fst, inl] + +@[simp] +theorem fst_comp_inl [DecidablePred fun x : G₀ ↦ x = 0] : + (fst ..).comp (inl G₀ H₀) = .id _ := + MonoidWithZeroHom.ext fun _ ↦ fst_inl _ + +@[simp] +theorem snd_comp_inl [DecidablePred fun x : G₀ ↦ x = 0] : + (snd ..).comp (inl G₀ H₀) = 1 := by + ext x + obtain rfl | ⟨_, rfl⟩ := GroupWithZero.eq_zero_or_unit x <;> + simp_all [WithZero.withZeroUnitsEquiv, snd, inl] + +theorem snd_inl_apply_of_ne_zero [DecidablePred fun x : G₀ ↦ x = 0] {x : G₀} (hx : x ≠ 0) : + snd _ _ (inl _ H₀ x) = 1 := by + rw [← MonoidWithZeroHom.comp_apply, snd_comp_inl, one_apply_of_ne_zero hx] + +@[simp] +theorem fst_comp_inr [DecidablePred fun x : H₀ ↦ x = 0] : + (fst ..).comp (inr G₀ H₀) = 1 := by + ext x + obtain rfl | ⟨_, rfl⟩ := GroupWithZero.eq_zero_or_unit x <;> + simp_all [WithZero.withZeroUnitsEquiv, fst, inr] + +theorem fst_inr_apply_of_ne_zero [DecidablePred fun x : H₀ ↦ x = 0] {x : H₀} (hx : x ≠ 0) : + fst _ _ (inr G₀ _ x) = 1 := by + rw [← MonoidWithZeroHom.comp_apply, fst_comp_inr, one_apply_of_ne_zero hx] + +@[simp] +theorem snd_inr [DecidablePred fun x : H₀ ↦ x = 0] (x : H₀) : + snd _ _ (inr G₀ _ x) = x := by + obtain rfl | ⟨_, rfl⟩ := GroupWithZero.eq_zero_or_unit x <;> + simp [WithZero.withZeroUnitsEquiv, snd, inr] + +@[simp] +theorem snd_comp_inr [DecidablePred fun x : H₀ ↦ x = 0] : + (snd ..).comp (inr G₀ H₀) = .id _ := + MonoidWithZeroHom.ext fun _ ↦ snd_inr _ + +lemma inl_injective [DecidablePred fun x : G₀ ↦ x = 0] : + Function.Injective (inl G₀ H₀) := + Function.HasLeftInverse.injective ⟨fst .., fun _ ↦ by simp⟩ +lemma inr_injective [DecidablePred fun x : H₀ ↦ x = 0] : + Function.Injective (inr G₀ H₀) := + Function.HasLeftInverse.injective ⟨snd .., fun _ ↦ by simp⟩ +lemma fst_surjective [DecidablePred fun x : G₀ ↦ x = 0] : + Function.Surjective (fst G₀ H₀) := + Function.HasRightInverse.surjective ⟨inl .., fun _ ↦ by simp⟩ +lemma snd_surjective [DecidablePred fun x : H₀ ↦ x = 0] : + Function.Surjective (snd G₀ H₀) := + Function.HasRightInverse.surjective ⟨inr .., fun _ ↦ by simp⟩ + +variable [DecidablePred fun x : G₀ ↦ x = 0] [DecidablePred fun x : H₀ ↦ x = 0] + +theorem inl_mul_inr_eq_mk_of_unit (m : G₀ˣ) (n : H₀ˣ) : + (inl G₀ H₀ m * inr G₀ H₀ n) = (m, n) := by + simp [inl, WithZero.withZeroUnitsEquiv, inr, ← WithZero.coe_mul] + +theorem commute_inl_inr (m : G₀) (n : H₀) : Commute (inl G₀ H₀ m) (inr G₀ H₀ n) := by + obtain rfl | ⟨_, rfl⟩ := GroupWithZero.eq_zero_or_unit m <;> + obtain rfl | ⟨_, rfl⟩ := GroupWithZero.eq_zero_or_unit n <;> + simp [inl, inr, WithZero.withZeroUnitsEquiv, commute_iff_eq, ← WithZero.coe_mul] + +end MonoidWithZeroHom diff --git a/Mathlib/Algebra/GroupWithZero/Units/Equiv.lean b/Mathlib/Algebra/GroupWithZero/Units/Equiv.lean index 78265850dda593..8b6d87f3716a25 100644 --- a/Mathlib/Algebra/GroupWithZero/Units/Equiv.lean +++ b/Mathlib/Algebra/GroupWithZero/Units/Equiv.lean @@ -23,8 +23,6 @@ elements. -/ @[simps] def _root_.unitsEquivNeZero : G₀ˣ ≃ {a : G₀ // a ≠ 0} where toFun a := ⟨a, a.ne_zero⟩ invFun a := Units.mk0 _ a.prop - left_inv _ := Units.ext rfl - right_inv _ := rfl /-- Left multiplication by a nonzero element in a `GroupWithZero` is a permutation of the underlying type. -/ diff --git a/Mathlib/Algebra/GroupWithZero/Units/Lemmas.lean b/Mathlib/Algebra/GroupWithZero/Units/Lemmas.lean index e8efcaa1b949e3..6edfeea50096ba 100644 --- a/Mathlib/Algebra/GroupWithZero/Units/Lemmas.lean +++ b/Mathlib/Algebra/GroupWithZero/Units/Lemmas.lean @@ -61,19 +61,31 @@ protected lemma inv_mul_eq_inv_mul_iff (hbd : Commute b d) (hb : b ≠ 0) (hd : end Commute -section MonoidWithZero +section MulZeroOneClass -variable [GroupWithZero G₀] [Nontrivial M₀] [MonoidWithZero M₀'] [FunLike F G₀ M₀] - [MonoidWithZeroHomClass F G₀ M₀] [FunLike F' G₀ M₀'] +variable [GroupWithZero G₀] [MulZeroOneClass M₀'] [Nontrivial M₀'] [FunLike F G₀ M₀'] + [MonoidWithZeroHomClass F G₀ M₀'] (f : F) {a : G₀} -theorem map_ne_zero : f a ≠ 0 ↔ a ≠ 0 := - ⟨fun hfa ha => hfa <| ha.symm ▸ map_zero f, fun ha => ((IsUnit.mk0 a ha).map f).ne_zero⟩ +theorem map_ne_zero : f a ≠ 0 ↔ a ≠ 0 := by + refine ⟨fun hfa ha => hfa <| ha.symm ▸ map_zero f, ?_⟩ + intro hx H + lift a to G₀ˣ using isUnit_iff_ne_zero.mpr hx + apply one_ne_zero (α := M₀') + rw [← map_one f, ← Units.mul_inv a, map_mul, H, zero_mul] @[simp] theorem map_eq_zero : f a = 0 ↔ a = 0 := not_iff_not.1 (map_ne_zero f) +end MulZeroOneClass + +section MonoidWithZero + +variable [GroupWithZero G₀] [Nontrivial M₀] [MonoidWithZero M₀'] [FunLike F G₀ M₀] + [MonoidWithZeroHomClass F G₀ M₀] [FunLike F' G₀ M₀'] + (f : F) {a : G₀} + theorem eq_on_inv₀ [MonoidWithZeroHomClass F' G₀ M₀'] (f g : F') (h : f a = g a) : f a⁻¹ = g a⁻¹ := by rcases eq_or_ne a 0 with (rfl | ha) diff --git a/Mathlib/Algebra/GroupWithZero/WithZero.lean b/Mathlib/Algebra/GroupWithZero/WithZero.lean index 7aa3ca2be9860c..e950a54b14be7c 100644 --- a/Mathlib/Algebra/GroupWithZero/WithZero.lean +++ b/Mathlib/Algebra/GroupWithZero/WithZero.lean @@ -22,6 +22,8 @@ This file proves that one can adjoin a new zero element to a group and get a gro a monoid homomorphism `f : α →* β`. -/ +open Function + assert_not_exists DenselyOrdered Ring namespace WithZero @@ -35,6 +37,11 @@ instance one : One (WithZero α) where @[simp, norm_cast] lemma coe_one : ((1 : α) : WithZero α) = 1 := rfl +@[simp] +lemma recZeroCoe_one {M N : Type*} [One M] (f : M → N) (z : N) : + recZeroCoe z f 1 = f 1 := + rfl + end One section Mul @@ -101,8 +108,6 @@ nonrec def lift' : (α →* β) ≃ (WithZero α →*₀ β) where | (_ : α), 0 => (mul_zero _).symm | (_ : α), (_ : α) => map_mul f _ _ } invFun F := F.toMonoidHom.comp coeMonoidHom - left_inv _ := rfl - right_inv _ := monoidWithZeroHom_ext rfl lemma lift'_zero (f : α →* β) : lift' f (0 : WithZero α) = 0 := rfl @@ -134,6 +139,11 @@ lemma map'_map' (f : α →* β) (g : β →* γ) (x) : map' g (map' f x) = map lemma map'_comp (f : α →* β) (g : β →* γ) : map' (g.comp f) = (map' g).comp (map' f) := MonoidWithZeroHom.ext fun x => (map'_map' f g x).symm +lemma map'_injective_iff {f : α →* β} : Injective (map' f) ↔ Injective f := by + simp [Injective, WithZero.forall] + +alias ⟨_, map'_injective⟩ := map'_injective_iff + end MulOneClass section Pow @@ -250,7 +260,6 @@ def unitsWithZeroEquiv : (WithZero α)ˣ ≃* α where toFun a := unzero a.ne_zero invFun a := Units.mk0 a coe_ne_zero left_inv _ := Units.ext <| by simp only [coe_unzero, Units.mk0_val] - right_inv _ := rfl map_mul' _ _ := coe_inj.mp <| by simp only [Units.val_mul, coe_unzero, coe_mul] theorem coe_unitsWithZeroEquiv_eq_units_val (γ : (WithZero α)ˣ) : @@ -268,6 +277,11 @@ def withZeroUnitsEquiv {G : Type*} [GroupWithZero G] right_inv _ := by simp only; split <;> simp_all map_mul' := (by induction · <;> induction · <;> simp [← WithZero.coe_mul]) +lemma withZeroUnitsEquiv_symm_apply_coe {G : Type*} [GroupWithZero G] + [DecidablePred (fun a : G ↦ a = 0)] (a : Gˣ) : + WithZero.withZeroUnitsEquiv.symm (a : G) = a := by + simp + /-- A version of `Equiv.optionCongr` for `WithZero`. -/ @[simps!] def _root_.MulEquiv.withZero [Group β] : @@ -297,3 +311,44 @@ instance instAddMonoidWithOne [AddMonoidWithOne α] : AddMonoidWithOne (WithZero natCast_succ n := by cases n <;> simp end WithZero + +namespace MonoidWithZeroHom + +protected lemma map_eq_zero_iff {G₀ G₀' : Type*} [GroupWithZero G₀] + [MulZeroOneClass G₀'] [Nontrivial G₀'] + {f : G₀ →*₀ G₀'} {x : G₀}: + f x = 0 ↔ x = 0 := by + refine ⟨?_, by simp +contextual⟩ + contrapose! + intro hx H + lift x to G₀ˣ using isUnit_iff_ne_zero.mpr hx + apply one_ne_zero (α := G₀') + rw [← map_one f, ← Units.mul_inv x, map_mul, H, zero_mul] + +variable {M₀ N₀} + +@[simp] +lemma one_apply_val_unit {M₀ N₀ : Type*} [MonoidWithZero M₀] [MulZeroOneClass N₀] + [DecidablePred fun x : M₀ ↦ x = 0] [Nontrivial M₀] [NoZeroDivisors M₀] (x : M₀ˣ) : + (1 : M₀ →*₀ N₀) x = (1 : N₀) := + one_apply_of_ne_zero x.ne_zero + +/-- The trivial group-with-zero hom is absorbing for composition. -/ +@[simp] +lemma apply_one_apply_eq {M₀ N₀ G₀ : Type*} [MulZeroOneClass M₀] [Nontrivial M₀] [NoZeroDivisors M₀] + [MulZeroOneClass N₀] [MulZeroOneClass G₀] [DecidablePred fun x : M₀ ↦ x = 0] + (f : N₀ →*₀ G₀) (x : M₀) : + f ((1 : M₀ →*₀ N₀) x) = (1 : M₀ →*₀ G₀) x := by + rcases eq_or_ne x 0 with rfl | hx + · simp + · rw [one_apply_of_ne_zero hx, one_apply_of_ne_zero hx, map_one] + +/-- The trivial group-with-zero hom is absorbing for composition. -/ +@[simp] +lemma comp_one {M₀ N₀ G₀ : Type*} [MulZeroOneClass M₀] [Nontrivial M₀] [NoZeroDivisors M₀] + [MulZeroOneClass N₀] [MulZeroOneClass G₀] [DecidablePred fun x : M₀ ↦ x = 0] + (f : N₀ →*₀ G₀) : + f.comp (1 : M₀ →*₀ N₀) = (1 : M₀ →*₀ G₀) := + ext <| apply_one_apply_eq _ + +end MonoidWithZeroHom diff --git a/Mathlib/Algebra/Homology/ComplexShapeSigns.lean b/Mathlib/Algebra/Homology/ComplexShapeSigns.lean index cfdbc3bc3140a9..dcee8573de2fa4 100644 --- a/Mathlib/Algebra/Homology/ComplexShapeSigns.lean +++ b/Mathlib/Algebra/Homology/ComplexShapeSigns.lean @@ -297,8 +297,6 @@ def symmetryEquiv (j : I₁₂) : (π c₂ c₁ c₁₂ ⁻¹' {j}) ≃ (π c₁ c₂ c₁₂ ⁻¹' {j}) where toFun := fun ⟨⟨i₂, i₁⟩, h⟩ => ⟨⟨i₁, i₂⟩, by simpa [π_symm] using h⟩ invFun := fun ⟨⟨i₁, i₂⟩, h⟩ => ⟨⟨i₂, i₁⟩, by simpa [π_symm] using h⟩ - left_inv _ := rfl - right_inv _ := rfl variable {c₁} diff --git a/Mathlib/Algebra/Homology/Embedding/Boundary.lean b/Mathlib/Algebra/Homology/Embedding/Boundary.lean index ba74cf27f8b413..0262ed70a2aa2f 100644 --- a/Mathlib/Algebra/Homology/Embedding/Boundary.lean +++ b/Mathlib/Algebra/Homology/Embedding/Boundary.lean @@ -184,7 +184,7 @@ lemma boundaryGE_embeddingUpIntGE_iff (p : ℤ) (n : ℕ) : omega lemma boundaryLE_embeddingUpIntLE_iff (p : ℤ) (n : ℕ) : - (embeddingUpIntGE p).BoundaryGE n ↔ n = 0 := by + (embeddingUpIntLE p).BoundaryLE n ↔ n = 0 := by constructor · intro h obtain _|n := n diff --git a/Mathlib/Algebra/Homology/ShortComplex/ModuleCat.lean b/Mathlib/Algebra/Homology/ShortComplex/ModuleCat.lean index 2b95dfcb0c9622..4e5c90c2d10a85 100644 --- a/Mathlib/Algebra/Homology/ShortComplex/ModuleCat.lean +++ b/Mathlib/Algebra/Homology/ShortComplex/ModuleCat.lean @@ -89,11 +89,8 @@ lemma Exact.moduleCat_of_range_eq_ker {X₁ X₂ X₃ : ModuleCat.{v} R} simpa only [moduleCat_exact_iff_range_eq_ker] using hfg /-- The canonical linear map `S.X₁ →ₗ[R] LinearMap.ker S.g` induced by `S.f`. -/ -@[simps] -def moduleCatToCycles : S.X₁ →ₗ[R] LinearMap.ker S.g.hom where - toFun x := ⟨S.f x, S.moduleCat_zero_apply x⟩ - map_add' x y := by aesop - map_smul' a x := by aesop +abbrev moduleCatToCycles : S.X₁ →ₗ[R] LinearMap.ker S.g.hom := + S.f.hom.codRestrict _ <| S.moduleCat_zero_apply /-- The explicit left homology data of a short complex of modules that is given by a kernel and a quotient given by the `LinearMap` API. The projections to `K` and `H` are diff --git a/Mathlib/Algebra/Homology/Square.lean b/Mathlib/Algebra/Homology/Square.lean index 427c27766f72d9..774d7fff375139 100644 --- a/Mathlib/Algebra/Homology/Square.lean +++ b/Mathlib/Algebra/Homology/Square.lean @@ -35,7 +35,6 @@ noncomputable def isPushoutEquivIsColimitCokernelCofork : Equiv.trans { toFun := fun h ↦ h.isColimit invFun := fun h ↦ IsPushout.mk _ h - left_inv := fun _ ↦ rfl right_inv := fun _ ↦ Subsingleton.elim _ _ } sq.commSq.isColimitEquivIsColimitCokernelCofork @@ -57,7 +56,6 @@ noncomputable def isPullbackEquivIsLimitKernelFork : Equiv.trans { toFun := fun h ↦ h.isLimit invFun := fun h ↦ IsPullback.mk _ h - left_inv := fun _ ↦ rfl right_inv := fun _ ↦ Subsingleton.elim _ _ } sq.commSq.isLimitEquivIsLimitKernelFork diff --git a/Mathlib/Algebra/Lie/Basic.lean b/Mathlib/Algebra/Lie/Basic.lean index 89224ed981718d..7ac7be8241793e 100644 --- a/Mathlib/Algebra/Lie/Basic.lean +++ b/Mathlib/Algebra/Lie/Basic.lean @@ -514,10 +514,10 @@ structure LieEquiv (R : Type u) (L : Type v) (L' : Type w) [CommRing R] [LieRing invFun : L' → L /-- The inverse function of an equivalence of Lie algebras is a left inverse of the underlying function. -/ - left_inv : Function.LeftInverse invFun toLieHom.toFun + left_inv : Function.LeftInverse invFun toLieHom.toFun := by intro; first | rfl | ext <;> rfl /-- The inverse function of an equivalence of Lie algebras is a right inverse of the underlying function. -/ - right_inv : Function.RightInverse invFun toLieHom.toFun + right_inv : Function.RightInverse invFun toLieHom.toFun := by intro; first | rfl | ext <;> rfl @[inherit_doc] notation:50 L " ≃ₗ⁅" R "⁆ " L' => LieEquiv R L L' diff --git a/Mathlib/Algebra/Lie/Character.lean b/Mathlib/Algebra/Lie/Character.lean index e71880b1f10b64..25faa36e53c46f 100644 --- a/Mathlib/Algebra/Lie/Character.lean +++ b/Mathlib/Algebra/Lie/Character.lean @@ -63,7 +63,5 @@ def lieCharacterEquivLinearDual [IsLieAbelian L] : LieCharacter R L ≃ Module.D map_lie' := fun {x y} => by rw [LieModule.IsTrivial.trivial, LieRing.of_associative_ring_bracket, mul_comm, sub_self, LinearMap.toFun_eq_coe, LinearMap.map_zero] } - left_inv χ := by ext; rfl - right_inv ψ := by ext; rfl end LieAlgebra diff --git a/Mathlib/Algebra/Lie/Submodule.lean b/Mathlib/Algebra/Lie/Submodule.lean index 8835e112d562a2..47798d6004f816 100644 --- a/Mathlib/Algebra/Lie/Submodule.lean +++ b/Mathlib/Algebra/Lie/Submodule.lean @@ -1077,9 +1077,7 @@ variable [LieAlgebra R L] [LieModule R L M] This is the Lie subalgebra version of `Submodule.topEquiv`. -/ def LieSubalgebra.topEquiv : (⊤ : LieSubalgebra R L) ≃ₗ⁅R⁆ L := { (⊤ : LieSubalgebra R L).incl with - invFun := fun x ↦ ⟨x, Set.mem_univ x⟩ - left_inv := fun x ↦ by ext; rfl - right_inv := fun _ ↦ rfl } + invFun := fun x ↦ ⟨x, Set.mem_univ x⟩ } @[simp] theorem LieSubalgebra.topEquiv_apply (x : (⊤ : LieSubalgebra R L)) : LieSubalgebra.topEquiv x = x := diff --git a/Mathlib/Algebra/Module/Equiv/Basic.lean b/Mathlib/Algebra/Module/Equiv/Basic.lean index ce7af45216d98d..493857631f2f2c 100644 --- a/Mathlib/Algebra/Module/Equiv/Basic.lean +++ b/Mathlib/Algebra/Module/Equiv/Basic.lean @@ -233,6 +233,10 @@ def toNatLinearEquiv : M ≃ₗ[ℕ] M₂ := theorem coe_toNatLinearEquiv : ⇑e.toNatLinearEquiv = e := rfl +@[simp] +theorem coe_symm_toNatLinearEquiv : ⇑e.toNatLinearEquiv.symm = e.symm := + rfl + @[simp] theorem toNatLinearEquiv_toAddEquiv : ↑e.toNatLinearEquiv = e := rfl @@ -243,7 +247,7 @@ theorem _root_.LinearEquiv.toAddEquiv_toNatLinearEquiv (e : M ≃ₗ[ℕ] M₂) DFunLike.coe_injective rfl @[simp] -theorem toNatLinearEquiv_symm : e.toNatLinearEquiv.symm = e.symm.toNatLinearEquiv := +theorem toNatLinearEquiv_symm : e.symm.toNatLinearEquiv = e.toNatLinearEquiv.symm := rfl @[simp] @@ -252,7 +256,7 @@ theorem toNatLinearEquiv_refl : (AddEquiv.refl M).toNatLinearEquiv = LinearEquiv @[simp] theorem toNatLinearEquiv_trans (e₂ : M₂ ≃+ M₃) : - e.toNatLinearEquiv.trans e₂.toNatLinearEquiv = (e.trans e₂).toNatLinearEquiv := + (e.trans e₂).toNatLinearEquiv = e.toNatLinearEquiv.trans e₂.toNatLinearEquiv := rfl end AddCommMonoid @@ -271,6 +275,10 @@ def toIntLinearEquiv : M ≃ₗ[ℤ] M₂ := theorem coe_toIntLinearEquiv : ⇑e.toIntLinearEquiv = e := rfl +@[simp] +theorem coe_symm_toIntLinearEquiv : ⇑e.toIntLinearEquiv.symm = e.symm := + rfl + @[simp] theorem toIntLinearEquiv_toAddEquiv : ↑e.toIntLinearEquiv = e := by ext @@ -282,7 +290,7 @@ theorem _root_.LinearEquiv.toAddEquiv_toIntLinearEquiv (e : M ≃ₗ[ℤ] M₂) DFunLike.coe_injective rfl @[simp] -theorem toIntLinearEquiv_symm : e.toIntLinearEquiv.symm = e.symm.toIntLinearEquiv := +theorem toIntLinearEquiv_symm : e.symm.toIntLinearEquiv = e.toIntLinearEquiv.symm := rfl @[simp] @@ -291,7 +299,7 @@ theorem toIntLinearEquiv_refl : (AddEquiv.refl M).toIntLinearEquiv = LinearEquiv @[simp] theorem toIntLinearEquiv_trans (e₂ : M₂ ≃+ M₃) : - e.toIntLinearEquiv.trans e₂.toIntLinearEquiv = (e.trans e₂).toIntLinearEquiv := + (e.trans e₂).toIntLinearEquiv = e.toIntLinearEquiv.trans e₂.toIntLinearEquiv := rfl end AddCommGroup @@ -333,8 +341,6 @@ def addMonoidHomLequivNat {A B : Type*} (R : Type*) [Semiring R] [AddCommMonoid invFun := LinearMap.toAddMonoidHom map_add' _ _ := rfl map_smul' _ _ := rfl - left_inv _ := rfl - right_inv _ := rfl /-- The `R`-linear equivalence between additive morphisms `A →+ B` and `ℤ`-linear morphisms `A →ₗ[ℤ] B`. @@ -346,8 +352,6 @@ def addMonoidHomLequivInt {A B : Type*} (R : Type*) [Semiring R] [AddCommGroup A invFun := LinearMap.toAddMonoidHom map_add' _ _ := rfl map_smul' _ _ := rfl - left_inv _ := rfl - right_inv _ := rfl /-- Ring equivalence between additive group endomorphisms of an `AddCommGroup` `A` and `ℤ`-module endomorphisms of `A.` -/ diff --git a/Mathlib/Algebra/Module/Equiv/Defs.lean b/Mathlib/Algebra/Module/Equiv/Defs.lean index f7c70d16302ca4..21c48f5ad35804 100644 --- a/Mathlib/Algebra/Module/Equiv/Defs.lean +++ b/Mathlib/Algebra/Module/Equiv/Defs.lean @@ -270,10 +270,15 @@ initialize_simps_projections LinearEquiv (toFun → apply, invFun → symm_apply theorem invFun_eq_symm : e.invFun = e.symm := rfl +theorem coe_toEquiv_symm : e.toEquiv.symm = e.symm := rfl + @[simp] -theorem coe_toEquiv_symm : e.toEquiv.symm = e.symm := +theorem toEquiv_symm : e.symm.toEquiv = e.toEquiv.symm := rfl +@[simp] +theorem coe_symm_toEquiv : ⇑e.toEquiv.symm = e.symm := rfl + variable {module_M₁ : Module R₁ M₁} {module_M₂ : Module R₂ M₂} {module_M₃ : Module R₃ M₃} variable {module_M₄ : Module R₄ M₄} {module_N₁ : Module R₁ N₁} {module_N₂ : Module R₁ N₂} variable {σ₁₂ : R₁ →+* R₂} {σ₂₁ : R₂ →+* R₁} @@ -312,7 +317,6 @@ notation3:80 (name := transNotation) e₁:80 " ≪≫ₗ " e₂:81 => variable {e₁₂} {e₂₃} -@[simp] theorem coe_toAddEquiv : e.toAddEquiv = e := rfl diff --git a/Mathlib/Algebra/Module/Presentation/DirectSum.lean b/Mathlib/Algebra/Module/Presentation/DirectSum.lean index 0c757dc87f03a1..014137a0090b3b 100644 --- a/Mathlib/Algebra/Module/Presentation/DirectSum.lean +++ b/Mathlib/Algebra/Module/Presentation/DirectSum.lean @@ -63,8 +63,6 @@ noncomputable def directSumEquiv : linearCombination_var_relation := fun ⟨i, r⟩ ↦ by rw [← (t i).linearCombination_var_relation r] apply Finsupp.linearCombination_embDomain } - left_inv _ := rfl - right_inv _ := rfl /-- Given `solution : ∀ (i : ι), (relations i).Solution (M i)`, this is the canonical solution of `Relations.directSum relations` in `⨁ i, M i`. -/ diff --git a/Mathlib/Algebra/Module/Presentation/Tautological.lean b/Mathlib/Algebra/Module/Presentation/Tautological.lean index 366f05371c0686..97e54cc2295400 100644 --- a/Mathlib/Algebra/Module/Presentation/Tautological.lean +++ b/Mathlib/Algebra/Module/Presentation/Tautological.lean @@ -54,8 +54,6 @@ noncomputable def tautologicalRelationsSolutionEquiv {N : Type w} [AddCommGroup invFun f := { var := f linearCombination_var_relation := by rintro (_ | _) <;> simp } - left_inv _ := rfl - right_inv _ := rfl /-- The obvious solution of `tautologicalRelations A M` in the module `M`. -/ @[simps! var] diff --git a/Mathlib/Algebra/Module/Submodule/Equiv.lean b/Mathlib/Algebra/Module/Submodule/Equiv.lean index 27359e19ebb69e..d55dade61c8793 100644 --- a/Mathlib/Algebra/Module/Submodule/Equiv.lean +++ b/Mathlib/Algebra/Module/Submodule/Equiv.lean @@ -95,9 +95,7 @@ variable (p) /-- The top submodule of `M` is linearly equivalent to `M`. -/ def ofTop (h : p = ⊤) : p ≃ₗ[R] M := { p.subtype with - invFun := fun x => ⟨x, h.symm ▸ trivial⟩ - left_inv := fun _ => rfl - right_inv := fun _ => rfl } + invFun := fun x => ⟨x, h.symm ▸ trivial⟩ } @[simp] theorem ofTop_apply {h} (x : p) : ofTop p h x = x := @@ -217,9 +215,7 @@ def equivSubtypeMap (p : Submodule R M) (q : Submodule R p) : q ≃ₗ[R] q.map { (p.subtype.domRestrict q).codRestrict _ (by rintro ⟨x, hx⟩; exact ⟨x, hx, rfl⟩) with invFun := by rintro ⟨x, hx⟩ - refine ⟨⟨x, ?_⟩, ?_⟩ <;> rcases hx with ⟨⟨_, h⟩, _, rfl⟩ <;> assumption - left_inv := fun ⟨⟨_, _⟩, _⟩ => rfl - right_inv := fun ⟨x, ⟨_, h⟩, _, rfl⟩ => by ext; rfl } + refine ⟨⟨x, ?_⟩, ?_⟩ <;> rcases hx with ⟨⟨_, h⟩, _, rfl⟩ <;> assumption } @[simp] theorem equivSubtypeMap_apply {p : Submodule R M} {q : Submodule R p} (x : q) : diff --git a/Mathlib/Algebra/Module/Submodule/Lattice.lean b/Mathlib/Algebra/Module/Submodule/Lattice.lean index 9d8e6cd8bdf210..ad9bffdb06efcf 100644 --- a/Mathlib/Algebra/Module/Submodule/Lattice.lean +++ b/Mathlib/Algebra/Module/Submodule/Lattice.lean @@ -108,8 +108,6 @@ def botEquivPUnit : (⊥ : Submodule R M) ≃ₗ[R] PUnit.{v+1} where invFun _ := 0 map_add' _ _ := rfl map_smul' _ _ := rfl - left_inv _ := Subsingleton.elim _ _ - right_inv _ := rfl theorem subsingleton_iff_eq_bot : Subsingleton p ↔ p = ⊥ := by rw [subsingleton_iff, Submodule.eq_bot_iff] @@ -164,8 +162,6 @@ def topEquiv : (⊤ : Submodule R M) ≃ₗ[R] M where invFun x := ⟨x, mem_top⟩ map_add' _ _ := rfl map_smul' _ _ := rfl - left_inv _ := rfl - right_inv _ := rfl /-! ## Infima & suprema in a submodule @@ -371,8 +367,6 @@ section NatSubmodule def AddSubmonoid.toNatSubmodule : AddSubmonoid M ≃o Submodule ℕ M where toFun S := { S with smul_mem' := fun r s hs ↦ show r • s ∈ S from nsmul_mem hs _ } invFun := Submodule.toAddSubmonoid - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := Iff.rfl @[simp] @@ -411,8 +405,6 @@ variable [AddCommGroup M] def AddSubgroup.toIntSubmodule : AddSubgroup M ≃o Submodule ℤ M where toFun S := { S with smul_mem' := fun _ _ hs ↦ S.zsmul_mem hs _ } invFun := Submodule.toAddSubgroup - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := Iff.rfl @[simp] @@ -430,7 +422,6 @@ theorem AddSubgroup.toIntSubmodule_toAddSubgroup (S : AddSubgroup M) : S.toIntSubmodule.toAddSubgroup = S := AddSubgroup.toIntSubmodule.symm_apply_apply S -@[simp] theorem Submodule.toAddSubgroup_toIntSubmodule (S : Submodule ℤ M) : S.toAddSubgroup.toIntSubmodule = S := AddSubgroup.toIntSubmodule.apply_symm_apply S diff --git a/Mathlib/Algebra/Module/Submodule/Map.lean b/Mathlib/Algebra/Module/Submodule/Map.lean index 9f0fa26816158c..f3d32cdd9390a9 100644 --- a/Mathlib/Algebra/Module/Submodule/Map.lean +++ b/Mathlib/Algebra/Module/Submodule/Map.lean @@ -268,8 +268,6 @@ def submoduleOf (p q : Submodule R M) : Submodule R q := def submoduleOfEquivOfLe {p q : Submodule R M} (h : p ≤ q) : p.submoduleOf q ≃ₗ[R] p where toFun m := ⟨m.1, m.2⟩ invFun m := ⟨⟨m.1, h m.2⟩, m.2⟩ - left_inv _ := Subtype.ext rfl - right_inv _ := Subtype.ext rfl map_add' _ _ := rfl map_smul' _ _ := rfl diff --git a/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean b/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean index 7923c4b6bec0ec..7604e4623470d0 100644 --- a/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean +++ b/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean @@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Nathaniel Thomas, Jeremy Avigad, Johannes Hölzl, Mario Carneiro, Andrew Yang, Johannes Hölzl, Kevin Buzzard, Yury Kudryashov -/ +import Mathlib.Algebra.Module.End import Mathlib.Algebra.Module.Submodule.Lattice import Mathlib.Order.Hom.CompleteLattice @@ -108,4 +109,9 @@ def restrictScalarsLatticeHom : CompleteLatticeHom (Submodule R M) (Submodule S map_sInf' s := by ext; simp map_sSup' s := by rw [← toAddSubmonoid_inj, toAddSubmonoid_sSup, ← Set.image_comp]; simp +@[simp] +lemma toIntSubmodule_toAddSubgroup {R M : Type*} [Ring R] [AddCommGroup M] [Module R M] + (N : Submodule R M) : + N.toAddSubgroup.toIntSubmodule = N.restrictScalars ℤ := rfl + end Submodule diff --git a/Mathlib/Algebra/Module/ZLattice/Covolume.lean b/Mathlib/Algebra/Module/ZLattice/Covolume.lean index 7810ba00e80e2e..db3e32b75c19f0 100644 --- a/Mathlib/Algebra/Module/ZLattice/Covolume.lean +++ b/Mathlib/Algebra/Module/ZLattice/Covolume.lean @@ -157,9 +157,7 @@ theorem volume_image_eq_volume_div_covolume' {E : Type*} [NormedAddCommGroup E] ← volume_image_eq_volume_div_covolume (ZLattice.comap ℝ L f.toLinearMap) (b.ofZLatticeComap ℝ L f.toLinearEquiv), Basis.ofZLatticeBasis_comap, ← f.image_symm_eq_preimage, ← Set.image_comp] - simp only [Basis.equivFun_apply, ContinuousLinearEquiv.symm_toLinearEquiv, Basis.map_equivFun, - LinearEquiv.symm_symm, Function.comp_apply, LinearEquiv.trans_apply, - ContinuousLinearEquiv.coe_toLinearEquiv, ContinuousLinearEquiv.apply_symm_apply] + simp end Basic diff --git a/Mathlib/Algebra/Module/ZMod.lean b/Mathlib/Algebra/Module/ZMod.lean index 59a19ead31b8e9..b2a4ad707eda3d 100644 --- a/Mathlib/Algebra/Module/ZMod.lean +++ b/Mathlib/Algebra/Module/ZMod.lean @@ -86,8 +86,6 @@ theorem coe_toZModLinearMap (f : M →+ M₁) : ⇑(f.toZModLinearMap n) = f := def toZModLinearMapEquiv : (M →+ M₁) ≃+ (M →ₗ[ZMod n] M₁) where toFun f := f.toZModLinearMap n invFun g := g - left_inv f := rfl - right_inv g := rfl map_add' f₁ f₂ := by ext; simp end AddMonoidHom @@ -100,8 +98,6 @@ See also: `AddSubgroup.toIntSubmodule`, `AddSubmonoid.toNatSubmodule`. -/ def toZModSubmodule : AddSubgroup M ≃o Submodule (ZMod n) M where toFun S := { S with smul_mem' := fun c _ h ↦ ZMod.smul_mem (K := S) h c } invFun := Submodule.toAddSubgroup - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := Iff.rfl @[simp] diff --git a/Mathlib/Algebra/MvPolynomial/Nilpotent.lean b/Mathlib/Algebra/MvPolynomial/Nilpotent.lean new file mode 100644 index 00000000000000..731c4a46b0f7ea --- /dev/null +++ b/Mathlib/Algebra/MvPolynomial/Nilpotent.lean @@ -0,0 +1,90 @@ +/- +Copyright (c) 2025 Andrew Yang. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Andrew Yang +-/ +import Mathlib.RingTheory.MvPolynomial.Homogeneous +import Mathlib.RingTheory.Polynomial.Nilpotent + +/-! +# Nilpotents and units in multivariate polynomial rings + +We prove that +- `MvPolynomial.isNilpotent_iff`: + A multivariate polynomial is nilpotent iff all its coefficents are. +- `MvPolynomial.isUnit_iff`: + A multivariate polynomial is invertible iff its constant term is invertible + and its other coefficients are nilpotent. +-/ + +namespace MvPolynomial + +variable {σ R : Type*} [CommRing R] {P : MvPolynomial σ R} + +-- Subsumed by `isNilpotent_iff` below. +private theorem isNilpotent_iff_of_fintype [Fintype σ] : + IsNilpotent P ↔ ∀ i, IsNilpotent (P.coeff i) := by + classical + refine Fintype.induction_empty_option ?_ ?_ ?_ σ P + · intros α β _ e h₁ P + rw [← IsNilpotent.map_iff (rename_injective _ e.symm.injective), h₁, + (Finsupp.equivCongrLeft e).forall_congr_left] + simp [Finsupp.equivMapDomain_eq_mapDomain, coeff_rename_mapDomain _ e.symm.injective] + · intro P + simp [Unique.forall_iff, ← IsNilpotent.map_iff (isEmptyRingEquiv R PEmpty).injective, + -isEmptyRingEquiv_apply, isEmptyRingEquiv_eq_coeff_zero] + rfl + · intro α _ H P + obtain ⟨P, rfl⟩ := (optionEquivLeft _ _).symm.surjective P + simp [IsNilpotent.map_iff (optionEquivLeft _ _).symm.injective, + Polynomial.isNilpotent_iff, H, Finsupp.optionEquiv.forall_congr_left, + ← optionEquivLeft_coeff_coeff, Finsupp.coe_update] + +theorem isNilpotent_iff : IsNilpotent P ↔ ∀ i, IsNilpotent (P.coeff i) := by + obtain ⟨n, f, hf, P, rfl⟩ := P.exists_fin_rename + rw [IsNilpotent.map_iff (rename_injective _ hf), MvPolynomial.isNilpotent_iff_of_fintype] + lift f to Fin n ↪ σ using hf + refine ⟨fun H i ↦ ?_, fun H i ↦ by simpa using H (i.embDomain f)⟩ + by_cases H : i ∈ Set.range (Finsupp.embDomain f) + · aesop + · rw [coeff_rename_eq_zero] <;> aesop (add simp Finsupp.embDomain_eq_mapDomain) + +instance [IsReduced R] : IsReduced (MvPolynomial σ R) := by + simp [isReduced_iff, isNilpotent_iff, MvPolynomial.ext_iff] + +theorem isUnit_iff : IsUnit P ↔ IsUnit (P.coeff 0) ∧ ∀ i ≠ 0, IsNilpotent (P.coeff i) := by + classical + refine ⟨fun H ↦ ⟨H.map constantCoeff, ?_⟩, fun ⟨h₁, h₂⟩ ↦ ?_⟩ + · intros n hn + obtain ⟨i, hi⟩ : ∃ i, n i ≠ 0 := by simpa [Finsupp.ext_iff] using hn + let e := (optionEquivLeft _ _).symm.trans (renameEquiv R (Equiv.optionSubtypeNe i)) + have H := (Polynomial.coeff_isUnit_isNilpotent_of_isUnit (H.map e.symm)).2 (n i) hi + simp only [ne_eq, isNilpotent_iff_eq_zero, isNilpotent_iff] at H + convert ← H (n.equivMapDomain (Equiv.optionSubtypeNe i).symm).some + refine (optionEquivLeft_coeff_coeff _ _ _ _).trans ?_ + simp [e, optionEquivLeft_coeff_coeff, Finsupp.equivMapDomain_eq_mapDomain, + coeff_rename_mapDomain _ (Equiv.optionSubtypeNe i).symm.injective] + · have : IsNilpotent (P - C (P.coeff 0)) := by + simp +contextual [isNilpotent_iff, apply_ite, eq_comm, h₂] + simpa using this.isUnit_add_right_of_commute (h₁.map C) (.all _ _) + +instance : IsLocalHom (C : _ →+* MvPolynomial σ R) where + map_nonunit := by classical simp +contextual [isUnit_iff, coeff_C, apply_ite] + +instance : IsLocalHom (algebraMap R (MvPolynomial σ R)) := + inferInstanceAs (IsLocalHom C) + +theorem isUnit_iff_totalDegree_of_isReduced [IsReduced R] : + IsUnit P ↔ IsUnit (P.coeff 0) ∧ P.totalDegree = 0 := by + convert isUnit_iff (P := P) + rw [totalDegree_eq_zero_iff] + simp [not_imp_comm (a := _ = (0 : R)), Finsupp.ext_iff] + +theorem isUnit_iff_eq_C_of_isReduced [IsReduced R] : + IsUnit P ↔ ∃ r, IsUnit r ∧ P = C r := by + rw [isUnit_iff_totalDegree_of_isReduced, totalDegree_eq_zero_iff_eq_C] + refine ⟨fun H ↦ ⟨_, H⟩, ?_⟩ + rintro ⟨r, hr, rfl⟩ + simpa + +end MvPolynomial diff --git a/Mathlib/Algebra/Order/Antidiag/Prod.lean b/Mathlib/Algebra/Order/Antidiag/Prod.lean index 84d79bc25debec..32c06e7b876198 100644 --- a/Mathlib/Algebra/Order/Antidiag/Prod.lean +++ b/Mathlib/Algebra/Order/Antidiag/Prod.lean @@ -181,7 +181,6 @@ def sigmaAntidiagonalEquivProd [AddMonoid A] [HasAntidiagonal A] : rintro ⟨n, ⟨k, l⟩, h⟩ rw [mem_antidiagonal] at h exact Sigma.subtype_ext h rfl - right_inv _ := rfl variable {A : Type*} [AddCommMonoid A] [PartialOrder A] [CanonicallyOrderedAdd A] diff --git a/Mathlib/Algebra/Order/Floor/Ring.lean b/Mathlib/Algebra/Order/Floor/Ring.lean index 00b2d42e85df69..fe234f0752707b 100644 --- a/Mathlib/Algebra/Order/Floor/Ring.lean +++ b/Mathlib/Algebra/Order/Floor/Ring.lean @@ -654,7 +654,7 @@ variable {k : Type*} [Field k] [LinearOrder k] [IsStrictOrderedRing k] [FloorRin lemma mul_lt_floor (hb₀ : 0 < b) (hb : b < 1) (hba : ⌈b / (1 - b)⌉ ≤ a) : b * a < ⌊a⌋ := by calc - b * a < b * (⌊a⌋ + 1) := by gcongr; exacts [hb₀, lt_floor_add_one _] + b * a < b * (⌊a⌋ + 1) := by gcongr; apply lt_floor_add_one _ ≤ ⌊a⌋ := by rwa [_root_.mul_add_one, ← le_sub_iff_add_le', ← one_sub_mul, ← div_le_iff₀' (by linarith), ← ceil_le, le_floor] @@ -678,7 +678,7 @@ lemma ceil_lt_mul (hb : 1 < b) (hba : ⌈(b - 1)⁻¹⌉ / b < a) : ⌈a⌉ < b calc ⌈a⌉ < a + 1 := ceil_lt_add_one _ _ = a + (b - 1) * (b - 1)⁻¹ := by rw [mul_inv_cancel₀]; positivity - _ ≤ a + (b - 1) * a := by gcongr; positivity + _ ≤ a + (b - 1) * a := by gcongr _ = b * a := by rw [sub_one_mul, add_sub_cancel] lemma ceil_le_mul (hb : 1 < b) (hba : ⌈(b - 1)⁻¹⌉ / b ≤ a) : ⌈a⌉ ≤ b * a := by diff --git a/Mathlib/Algebra/Order/Floor/Semiring.lean b/Mathlib/Algebra/Order/Floor/Semiring.lean index 1d6786fa52dc54..0741ec4e0a8da0 100644 --- a/Mathlib/Algebra/Order/Floor/Semiring.lean +++ b/Mathlib/Algebra/Order/Floor/Semiring.lean @@ -428,7 +428,7 @@ variable [Field K] [LinearOrder K] [IsStrictOrderedRing K] [FloorSemiring K] {a lemma mul_lt_floor (hb₀ : 0 < b) (hb : b < 1) (hba : ⌈b / (1 - b)⌉₊ ≤ a) : b * a < ⌊a⌋₊ := by calc - b * a < b * (⌊a⌋₊ + 1) := by gcongr; exacts [hb₀, lt_floor_add_one _] + b * a < b * (⌊a⌋₊ + 1) := by gcongr; apply lt_floor_add_one _ ≤ ⌊a⌋₊ := by rw [_root_.mul_add_one, ← le_sub_iff_add_le', ← one_sub_mul, ← div_le_iff₀' (by linarith), ← ceil_le] @@ -443,7 +443,7 @@ lemma ceil_lt_mul (hb : 1 < b) (hba : ⌈(b - 1)⁻¹⌉₊ / b < a) : ⌈a⌉ calc ⌈a⌉₊ < a + 1 := ceil_lt_add_one <| hba.trans' <| by positivity _ = a + (b - 1) * (b - 1)⁻¹ := by rw [mul_inv_cancel₀]; positivity - _ ≤ a + (b - 1) * a := by gcongr; positivity + _ ≤ a + (b - 1) * a := by gcongr _ = b * a := by rw [sub_one_mul, add_sub_cancel] lemma ceil_le_mul (hb : 1 < b) (hba : ⌈(b - 1)⁻¹⌉₊ / b ≤ a) : ⌈a⌉₊ ≤ b * a := by diff --git a/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean b/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean index e4fed8de69496a..49497dd60b9ac4 100644 --- a/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean +++ b/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean @@ -26,7 +26,7 @@ whereas it is a very common target for valuations. The solutions is to use a typeclass, and that is exactly what we do in this file. -/ -variable {α : Type*} +variable {α β : Type*} /-- A linearly ordered commutative monoid with a zero element. -/ class LinearOrderedCommMonoidWithZero (α : Type*) extends CommMonoidWithZero α, LinearOrder α, @@ -250,14 +250,16 @@ instance [LinearOrderedAddCommGroupWithTop α] : namespace WithZero section Preorder -variable [Preorder α] {a b : α} +variable [Preorder α] [Preorder β] {x : WithZero α} {a b : α} instance instPreorder : Preorder (WithZero α) := WithBot.preorder instance instOrderBot : OrderBot (WithZero α) := WithBot.orderBot -lemma zero_le (a : WithZero α) : 0 ≤ a := bot_le +@[simp] lemma zero_le (a : WithZero α) : 0 ≤ a := bot_le -lemma zero_lt_coe (a : α) : (0 : WithZero α) < a := WithBot.bot_lt_coe a +@[simp] lemma zero_lt_coe (a : α) : (0 : WithZero α) < a := WithBot.bot_lt_coe a +@[simp] lemma not_coe_le_zero : ¬ a ≤ (0 : WithZero α) := WithBot.not_coe_le_bot a +@[simp] lemma not_lt_zero : ¬ x < (0 : WithZero α) := WithBot.not_lt_bot _ lemma zero_eq_bot : (0 : WithZero α) = ⊥ := rfl @@ -318,6 +320,12 @@ instance instExistsAddOfLE [Add α] [ExistsAddOfLE α] : ExistsAddOfLE (WithZero obtain ⟨c, rfl⟩ := exists_add_of_le (WithZero.coe_le_coe.1 h) exact ⟨c, rfl⟩⟩ +lemma map'_mono [MulOneClass α] [MulOneClass β] {f : α →* β} (hf : Monotone f) : + Monotone (map' f) := by simpa [Monotone, WithZero.forall] + +lemma map'_strictMono [MulOneClass α] [MulOneClass β] {f : α →* β} (hf : StrictMono f) : + StrictMono (map' f) := by simpa [StrictMono, WithZero.forall] + end Preorder section PartialOrder diff --git a/Mathlib/Algebra/Order/GroupWithZero/Unbundled/OrderIso.lean b/Mathlib/Algebra/Order/GroupWithZero/Unbundled/OrderIso.lean index 83de544edfa5d5..0019dde88e31bd 100644 --- a/Mathlib/Algebra/Order/GroupWithZero/Unbundled/OrderIso.lean +++ b/Mathlib/Algebra/Order/GroupWithZero/Unbundled/OrderIso.lean @@ -11,8 +11,11 @@ import Mathlib.Order.Hom.Basic # Multiplication by a positive element as an order isomorphism -/ +variable {G₀ : Type*} [GroupWithZero G₀] + namespace OrderIso -variable {G₀} [GroupWithZero G₀] [PartialOrder G₀] + +variable [PartialOrder G₀] section left variable [PosMulReflectLT G₀] @@ -55,3 +58,30 @@ def divRight₀ (a : G₀) (ha : 0 < a) : G₀ ≃o G₀ where end right end OrderIso +section Lattice + +lemma mul_inf₀ [SemilatticeInf G₀] [PosMulReflectLT G₀] {c : G₀} (hc : 0 ≤ c) (a b : G₀) : + c * (a ⊓ b) = c * a ⊓ c * b := by + obtain (rfl|hc) := hc.eq_or_lt + · simp + · exact (OrderIso.mulLeft₀ c hc).map_inf a b + +lemma mul_sup₀ [SemilatticeSup G₀] [PosMulReflectLT G₀] {c : G₀} (hc : 0 ≤ c) (a b : G₀) : + c * (a ⊔ b) = c * a ⊔ c * b := by + obtain (rfl|hc) := hc.eq_or_lt + · simp + · exact (OrderIso.mulLeft₀ c hc).map_sup a b + +lemma inf_mul₀ [SemilatticeInf G₀] [MulPosReflectLT G₀] {c : G₀} (hc : 0 ≤ c) (a b : G₀) : + (a ⊓ b) * c = a * c ⊓ b * c := by + obtain (rfl|hc) := hc.eq_or_lt + · simp + · exact (OrderIso.mulRight₀ c hc).map_inf a b + +lemma sup_mul₀ [SemilatticeSup G₀] [MulPosReflectLT G₀] {c : G₀} (hc : 0 ≤ c) (a b : G₀) : + (a ⊔ b) * c = a * c ⊔ b * c := by + obtain (rfl|hc) := hc.eq_or_lt + · simp + · exact (OrderIso.mulRight₀ c hc).map_sup a b + +end Lattice diff --git a/Mathlib/Algebra/Order/Module/OrderedSMul.lean b/Mathlib/Algebra/Order/Module/OrderedSMul.lean index e9ed9c6ae68c55..3c9def738f0654 100644 --- a/Mathlib/Algebra/Order/Module/OrderedSMul.lean +++ b/Mathlib/Algebra/Order/Module/OrderedSMul.lean @@ -7,7 +7,6 @@ import Mathlib.Algebra.Group.Action.Basic import Mathlib.Algebra.Module.Pi import Mathlib.Algebra.Module.Prod import Mathlib.Algebra.Order.Module.Defs -import Mathlib.Tactic.GCongr.CoreAttrs /-! # Ordered scalar product diff --git a/Mathlib/Algebra/Order/Monoid/Canonical/Defs.lean b/Mathlib/Algebra/Order/Monoid/Canonical/Defs.lean index d2f5d30c170102..23d0f96360b057 100644 --- a/Mathlib/Algebra/Order/Monoid/Canonical/Defs.lean +++ b/Mathlib/Algebra/Order/Monoid/Canonical/Defs.lean @@ -309,6 +309,10 @@ instance (priority := 10) of_gt' {M : Type*} [AddZeroClass M] [Preorder M] [Cano [One M] {y : M} [Fact (1 < y)] : NeZero y := of_gt <| @Fact.out (1 < y) _ +theorem of_ge {M} [AddZeroClass M] [PartialOrder M] [CanonicallyOrderedAdd M] + {x y : M} [NeZero x] (h : x ≤ y) : NeZero y := + of_pos <| lt_of_lt_of_le (pos x) h + end NeZero set_option linter.deprecated false in diff --git a/Mathlib/Algebra/Order/Ring/Idempotent.lean b/Mathlib/Algebra/Order/Ring/Idempotent.lean index 1b414eef859e3f..d55e0a18de8dac 100644 --- a/Mathlib/Algebra/Order/Ring/Idempotent.lean +++ b/Mathlib/Algebra/Order/Ring/Idempotent.lean @@ -142,7 +142,6 @@ def OrderIso.isIdempotentElemMulZeroAddOne : {a : R // IsIdempotentElem a} ≃o {a : R × R // a.1 * a.2 = 0 ∧ a.1 + a.2 = 1} where toFun a := ⟨(a, 1 - a), by simp_rw [mul_sub, mul_one, a.2.eq, sub_self], by rw [add_sub_cancel]⟩ invFun a := ⟨a.1.1, (IsIdempotentElem.of_mul_add a.2.1 a.2.2).1⟩ - left_inv _ := rfl right_inv a := Subtype.ext <| Prod.ext rfl <| sub_eq_of_eq_add <| a.2.2.symm.trans (add_comm ..) map_rel_iff' := Iff.rfl diff --git a/Mathlib/Algebra/Polynomial/Basic.lean b/Mathlib/Algebra/Polynomial/Basic.lean index 563df7125dcee4..14cd9cddb83507 100644 --- a/Mathlib/Algebra/Polynomial/Basic.lean +++ b/Mathlib/Algebra/Polynomial/Basic.lean @@ -340,8 +340,6 @@ implementation detail, but it can be useful to transfer results from `Finsupp` t def toFinsuppIso : R[X] ≃+* R[ℕ] where toFun := toFinsupp invFun := ofFinsupp - left_inv := fun ⟨_p⟩ => rfl - right_inv _p := rfl map_mul' := toFinsupp_mul map_add' := toFinsupp_add @@ -1052,6 +1050,17 @@ theorem coeff_mem_coeffs (p : R[X]) (n : ℕ) (h : p.coeff n ≠ 0) : p.coeff n simp only [coeffs, exists_prop, mem_support_iff, Finset.mem_image, Ne] exact ⟨n, h, rfl⟩ +@[simp] +theorem coeffs_empty_iff (p : R[X]) : coeffs p = ∅ ↔ p = 0 := by + refine ⟨?_, fun h ↦ by simp [h]⟩ + contrapose! + intro h + rw [← support_nonempty] at h + obtain ⟨n, hn⟩ := h + rw [mem_support_iff] at hn + rw [← nonempty_iff_ne_empty] + exact ⟨p.coeff n, coeff_mem_coeffs p n hn⟩ + theorem coeffs_monomial (n : ℕ) {c : R} (hc : c ≠ 0) : (monomial n c).coeffs = {c} := by rw [coeffs, support_monomial n hc] simp diff --git a/Mathlib/Algebra/Polynomial/Div.lean b/Mathlib/Algebra/Polynomial/Div.lean index d796a1432ed864..d9883325c23b23 100644 --- a/Mathlib/Algebra/Polynomial/Div.lean +++ b/Mathlib/Algebra/Polynomial/Div.lean @@ -617,7 +617,8 @@ theorem eval₂_modByMonic_eq_self_of_root [CommRing S] {f : R →+* S} {p q : R theorem sub_dvd_eval_sub (a b : R) (p : R[X]) : a - b ∣ p.eval a - p.eval b := by suffices X - C b ∣ p - C (p.eval b) by - simpa only [coe_evalRingHom, eval_sub, eval_X, eval_C] using (evalRingHom a).map_dvd this + simpa only [coe_evalRingHom, eval_sub, eval_X, eval_C] + using (_root_.map_dvd (evalRingHom a)) this simp [dvd_iff_isRoot] @[simp] diff --git a/Mathlib/Algebra/Polynomial/EraseLead.lean b/Mathlib/Algebra/Polynomial/EraseLead.lean index f9ca512f7ebd7f..e899c007f9002e 100644 --- a/Mathlib/Algebra/Polynomial/EraseLead.lean +++ b/Mathlib/Algebra/Polynomial/EraseLead.lean @@ -366,7 +366,7 @@ theorem card_support_eq {n : ℕ} : Function.extend Fin.castSucc x fun _ => f.leadingCoeff, ?_, ?_, ?_⟩ · intro i j hij have hi : i ∈ Set.range (Fin.castSucc : Fin n → Fin (n + 1)) := by - rw [Fin.range_castSucc, Set.mem_def] + simp only [Fin.range_castSucc, Nat.succ_eq_add_one, Set.mem_setOf_eq] exact lt_of_lt_of_le hij (Nat.lt_succ_iff.mp j.2) obtain ⟨i, rfl⟩ := hi rw [Fin.strictMono_castSucc.injective.extend_apply] diff --git a/Mathlib/Algebra/Polynomial/Eval/Defs.lean b/Mathlib/Algebra/Polynomial/Eval/Defs.lean index 2ba050083966e8..45c1e01981e437 100644 --- a/Mathlib/Algebra/Polynomial/Eval/Defs.lean +++ b/Mathlib/Algebra/Polynomial/Eval/Defs.lean @@ -215,7 +215,7 @@ theorem eval₂_pow (n : ℕ) : (p ^ n).eval₂ f x = p.eval₂ f x ^ n := (eval₂RingHom _ _).map_pow _ _ theorem eval₂_dvd : p ∣ q → eval₂ f x p ∣ eval₂ f x q := - (eval₂RingHom f x).map_dvd + map_dvd (eval₂RingHom f x) theorem eval₂_eq_zero_of_dvd_of_eval₂_eq_zero (h : p ∣ q) (h0 : eval₂ f x p = 0) : eval₂ f x q = 0 := @@ -524,7 +524,7 @@ protected theorem map_ofNat (n : ℕ) [n.AtLeastTwo] : --TODO rename to `map_dvd_map` theorem map_dvd (f : R →+* S) {x y : R[X]} : x ∣ y → x.map f ∣ y.map f := - (mapRingHom f).map_dvd + _root_.map_dvd (mapRingHom f) lemma mapRingHom_comp_C {R S : Type*} [Semiring R] [Semiring S] (f : R →+* S) : (mapRingHom f).comp C = C.comp f := by ext; simp diff --git a/Mathlib/Algebra/Polynomial/Module/AEval.lean b/Mathlib/Algebra/Polynomial/Module/AEval.lean index 62253978f2166e..d9ac4edb718fc5 100644 --- a/Mathlib/Algebra/Polynomial/Module/AEval.lean +++ b/Mathlib/Algebra/Polynomial/Module/AEval.lean @@ -157,8 +157,6 @@ def equiv_mapSubmodule : p ≃ₗ[R] mapSubmodule R M a ⟨p, hp⟩ where toFun x := ⟨of R M a x, by simp⟩ invFun x := ⟨((of R M _).symm (x : AEval R M a)), by obtain ⟨x, hx⟩ := x; simpa using hx⟩ - left_inv x := rfl - right_inv x := rfl map_add' x y := rfl map_smul' t x := rfl diff --git a/Mathlib/Algebra/Polynomial/Roots.lean b/Mathlib/Algebra/Polynomial/Roots.lean index 9557faff5c2142..78d91847a7424e 100644 --- a/Mathlib/Algebra/Polynomial/Roots.lean +++ b/Mathlib/Algebra/Polynomial/Roots.lean @@ -729,7 +729,7 @@ variable {A B : Type*} [CommRing A] [CommRing B] theorem le_rootMultiplicity_map {p : A[X]} {f : A →+* B} (hmap : map f p ≠ 0) (a : A) : rootMultiplicity a p ≤ rootMultiplicity (f a) (p.map f) := by rw [le_rootMultiplicity_iff hmap] - refine _root_.trans ?_ ((mapRingHom f).map_dvd (pow_rootMultiplicity_dvd p a)) + refine _root_.trans ?_ (_root_.map_dvd (mapRingHom f) (pow_rootMultiplicity_dvd p a)) rw [map_pow, map_sub, coe_mapRingHom, map_X, map_C] theorem eq_rootMultiplicity_map {p : A[X]} {f : A →+* B} (hf : Function.Injective f) (a : A) : diff --git a/Mathlib/Algebra/Quandle.lean b/Mathlib/Algebra/Quandle.lean index 96d6e9b9f713f8..1ee16938ccef97 100644 --- a/Mathlib/Algebra/Quandle.lean +++ b/Mathlib/Algebra/Quandle.lean @@ -661,7 +661,6 @@ def toEnvelGroup.map {R : Type*} [Rack R] {G : Type*} [Group G] : change Quotient.liftOn ⟦mul x y⟧ (toEnvelGroup.mapAux f) _ = _ simp [toEnvelGroup.mapAux] } invFun F := (Quandle.Conj.map F).comp (toEnvelGroup R) - left_inv f := by ext; rfl right_inv F := MonoidHom.ext fun x => Quotient.inductionOn x fun x => by diff --git a/Mathlib/Algebra/Quaternion.lean b/Mathlib/Algebra/Quaternion.lean index 2c6f1734490f4b..6a2e5cd481cb3d 100644 --- a/Mathlib/Algebra/Quaternion.lean +++ b/Mathlib/Algebra/Quaternion.lean @@ -80,16 +80,13 @@ open Quaternion def equivProd {R : Type*} (c₁ c₂ c₃ : R) : ℍ[R,c₁,c₂,c₃] ≃ R × R × R × R where toFun a := ⟨a.1, a.2, a.3, a.4⟩ invFun a := ⟨a.1, a.2.1, a.2.2.1, a.2.2.2⟩ - left_inv _ := rfl - right_inv _ := rfl /-- The equivalence between a quaternion algebra over `R` and `Fin 4 → R`. -/ @[simps symm_apply] def equivTuple {R : Type*} (c₁ c₂ c₃ : R) : ℍ[R,c₁,c₂,c₃] ≃ (Fin 4 → R) where toFun a := ![a.1, a.2, a.3, a.4] invFun a := ⟨a 0, a 1, a 2, a 3⟩ - left_inv _ := rfl - right_inv f := by ext ⟨_, _ | _ | _ | _ | _ | ⟨⟩⟩ <;> rfl + right_inv _ := by ext ⟨_, _ | _ | _ | _ | _ | ⟨⟩⟩ <;> rfl @[simp] theorem equivTuple_apply {R : Type*} (c₁ c₂ c₃ : R) (x : ℍ[R,c₁,c₂,c₃]) : diff --git a/Mathlib/Algebra/Ring/Equiv.lean b/Mathlib/Algebra/Ring/Equiv.lean index cab85052362661..78dbd1a2090e72 100644 --- a/Mathlib/Algebra/Ring/Equiv.lean +++ b/Mathlib/Algebra/Ring/Equiv.lean @@ -380,12 +380,6 @@ protected def op {α β} [Add α] [Mul α] [Add β] [Mul β] : α ≃+* β ≃ (αᵐᵒᵖ ≃+* βᵐᵒᵖ) where toFun f := { AddEquiv.mulOp f.toAddEquiv, MulEquiv.op f.toMulEquiv with } invFun f := { AddEquiv.mulOp.symm f.toAddEquiv, MulEquiv.op.symm f.toMulEquiv with } - left_inv f := by - ext - rfl - right_inv f := by - ext - rfl /-- The 'unopposite' of a ring iso `αᵐᵒᵖ ≃+* βᵐᵒᵖ`. Inverse to `RingEquiv.op`. -/ @[simp] diff --git a/Mathlib/Algebra/Ring/Hom/Basic.lean b/Mathlib/Algebra/Ring/Hom/Basic.lean index 6a13790b05b034..a03b3590d97d15 100644 --- a/Mathlib/Algebra/Ring/Hom/Basic.lean +++ b/Mathlib/Algebra/Ring/Hom/Basic.lean @@ -3,54 +3,15 @@ Copyright (c) 2019 Amelia Livingston. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Amelia Livingston, Jireh Loreaux -/ -import Mathlib.Algebra.Divisibility.Hom -import Mathlib.Algebra.GroupWithZero.InjSurj + import Mathlib.Algebra.Ring.Hom.Defs -import Mathlib.Data.Set.Insert /-! # Additional lemmas about homomorphisms of semirings and rings - -These lemmas have been banished here to avoid unnecessary imports in -`Mathlib/Algebra/Hom/Ring/Defs.lean`. - -They could be moved to more natural homes. -/ assert_not_exists RelIso Field -open Function - -variable {α β : Type*} - namespace RingHom -section - -variable {_ : NonAssocSemiring α} {_ : NonAssocSemiring β} (f : α →+* β) - -/-- `f : α →+* β` has a trivial codomain iff its range is `{0}`. -/ -theorem codomain_trivial_iff_range_eq_singleton_zero : (0 : β) = 1 ↔ Set.range f = {0} := - f.codomain_trivial_iff_range_trivial.trans - ⟨fun h => - Set.ext fun y => ⟨fun ⟨x, hx⟩ => by simp [← hx, h x], fun hy => ⟨0, by simpa using hy.symm⟩⟩, - fun h x => Set.mem_singleton_iff.mp (h ▸ Set.mem_range_self x)⟩ - -end - -section Semiring - -variable [Semiring α] [Semiring β] - -protected theorem map_dvd (f : α →+* β) {a b : α} : a ∣ b → f a ∣ f b := - map_dvd f - -end Semiring - end RingHom - -/-- Pullback `IsDomain` instance along an injective function. -/ -protected theorem Function.Injective.isDomain [Semiring α] [IsDomain α] [Semiring β] {F} - [FunLike F β α] [MonoidWithZeroHomClass F β α] (f : F) (hf : Injective f) : IsDomain β where - __ := domain_nontrivial f (map_zero _) (map_one _) - __ := hf.isCancelMulZero f (map_zero _) (map_mul _) diff --git a/Mathlib/Algebra/Ring/Hom/InjSurj.lean b/Mathlib/Algebra/Ring/Hom/InjSurj.lean new file mode 100644 index 00000000000000..9e413df2e23275 --- /dev/null +++ b/Mathlib/Algebra/Ring/Hom/InjSurj.lean @@ -0,0 +1,22 @@ +/- +Copyright (c) 2019 Amelia Livingston. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Amelia Livingston, Jireh Loreaux +-/ +import Mathlib.Algebra.GroupWithZero.Hom +import Mathlib.Algebra.GroupWithZero.InjSurj +import Mathlib.Algebra.Ring.Defs + +/-! +# Pulling back rings along injective maps, and pushing them forward along surjective maps +-/ + +open Function + +variable {α β : Type*} + +/-- Pullback `IsDomain` instance along an injective function. -/ +protected theorem Function.Injective.isDomain [Semiring α] [IsDomain α] [Semiring β] {F} + [FunLike F β α] [MonoidWithZeroHomClass F β α] (f : F) (hf : Injective f) : IsDomain β where + __ := domain_nontrivial f (map_zero _) (map_one _) + __ := hf.isCancelMulZero f (map_zero _) (map_mul _) diff --git a/Mathlib/Algebra/Ring/Opposite.lean b/Mathlib/Algebra/Ring/Opposite.lean index 572fa6888e3625..e812c0d82bd8d5 100644 --- a/Mathlib/Algebra/Ring/Opposite.lean +++ b/Mathlib/Algebra/Ring/Opposite.lean @@ -166,8 +166,6 @@ def NonUnitalRingHom.op {R S} [NonUnitalNonAssocSemiring R] [NonUnitalNonAssocSe (R →ₙ+* S) ≃ (Rᵐᵒᵖ →ₙ+* Sᵐᵒᵖ) where toFun f := { AddMonoidHom.mulOp f.toAddMonoidHom, MulHom.op f.toMulHom with } invFun f := { AddMonoidHom.mulUnop f.toAddMonoidHom, MulHom.unop f.toMulHom with } - left_inv _ := rfl - right_inv _ := rfl /-- The 'unopposite' of a non-unital ring hom `Rᵐᵒᵖ →ₙ+* Sᵐᵒᵖ`. Inverse to `NonUnitalRingHom.op`. -/ @@ -199,8 +197,6 @@ def RingHom.op {R S} [NonAssocSemiring R] [NonAssocSemiring S] : (R →+* S) ≃ (Rᵐᵒᵖ →+* Sᵐᵒᵖ) where toFun f := { AddMonoidHom.mulOp f.toAddMonoidHom, MonoidHom.op f.toMonoidHom with } invFun f := { AddMonoidHom.mulUnop f.toAddMonoidHom, MonoidHom.unop f.toMonoidHom with } - left_inv _ := rfl - right_inv _ := rfl /-- The 'unopposite' of a ring hom `Rᵐᵒᵖ →+* Sᵐᵒᵖ`. Inverse to `RingHom.op`. -/ @[simp] diff --git a/Mathlib/Algebra/Ring/Prod.lean b/Mathlib/Algebra/Ring/Prod.lean index 5280a25b4b5be4..0af23b585b41fc 100644 --- a/Mathlib/Algebra/Ring/Prod.lean +++ b/Mathlib/Algebra/Ring/Prod.lean @@ -314,7 +314,6 @@ def prodZeroRing : R ≃+* R × S where invFun := Prod.fst map_add' := by simp map_mul' := by simp - left_inv _ := rfl right_inv x := by cases x; simp [eq_iff_true_of_subsingleton] /-- A ring `R` is isomorphic to `S × R` when `S` is the zero ring -/ @@ -324,7 +323,6 @@ def zeroRingProd : R ≃+* S × R where invFun := Prod.snd map_add' := by simp map_mul' := by simp - left_inv _ := rfl right_inv x := by cases x; simp [eq_iff_true_of_subsingleton] end RingEquiv diff --git a/Mathlib/Algebra/Ring/Subsemiring/Basic.lean b/Mathlib/Algebra/Ring/Subsemiring/Basic.lean index 077aeca1e08fca..ac1444cafc78bf 100644 --- a/Mathlib/Algebra/Ring/Subsemiring/Basic.lean +++ b/Mathlib/Algebra/Ring/Subsemiring/Basic.lean @@ -96,8 +96,6 @@ protected theorem sum_mem (s : Subsemiring R) {ι : Type*} {t : Finset ι} {f : def topEquiv : (⊤ : Subsemiring R) ≃+* R where toFun r := r invFun r := ⟨r, Subsemiring.mem_top r⟩ - left_inv _ := rfl - right_inv _ := rfl map_mul' := (⊤ : Subsemiring R).coe_mul map_add' := (⊤ : Subsemiring R).coe_add diff --git a/Mathlib/Algebra/SkewMonoidAlgebra/Basic.lean b/Mathlib/Algebra/SkewMonoidAlgebra/Basic.lean index 4a4bbb7d8a4ab5..9b288761223e03 100644 --- a/Mathlib/Algebra/SkewMonoidAlgebra/Basic.lean +++ b/Mathlib/Algebra/SkewMonoidAlgebra/Basic.lean @@ -260,8 +260,6 @@ to `SkewMonoidAlgebra`. -/ def toFinsuppAddEquiv : SkewMonoidAlgebra k G ≃+ (G →₀ k) where toFun := toFinsupp invFun := ofFinsupp - left_inv := fun ⟨_p⟩ ↦ rfl - right_inv _p := rfl map_add' := toFinsupp_add theorem smul_single {S} [SMulZeroClass S k] (s : S) (a : G) (b : k) : diff --git a/Mathlib/Algebra/Star/StarAlgHom.lean b/Mathlib/Algebra/Star/StarAlgHom.lean index df222495515c54..08bda4862282f1 100644 --- a/Mathlib/Algebra/Star/StarAlgHom.lean +++ b/Mathlib/Algebra/Star/StarAlgHom.lean @@ -517,8 +517,6 @@ their codomains. -/ def prodEquiv : (A →⋆ₙₐ[R] B) × (A →⋆ₙₐ[R] C) ≃ (A →⋆ₙₐ[R] B × C) where toFun f := f.1.prod f.2 invFun f := ((fst _ _ _).comp f, (snd _ _ _).comp f) - left_inv f := by ext <;> rfl - right_inv f := by ext <;> rfl end Prod @@ -623,8 +621,6 @@ their codomains. -/ def prodEquiv : (A →⋆ₐ[R] B) × (A →⋆ₐ[R] C) ≃ (A →⋆ₐ[R] B × C) where toFun f := f.1.prod f.2 invFun f := ((fst _ _ _).comp f, (snd _ _ _).comp f) - left_inv f := by ext <;> rfl - right_inv f := by ext <;> rfl end StarAlgHom diff --git a/Mathlib/Algebra/TrivSqZeroExt.lean b/Mathlib/Algebra/TrivSqZeroExt.lean index 1939df9ecf069c..d43807802ddcc4 100644 --- a/Mathlib/Algebra/TrivSqZeroExt.lean +++ b/Mathlib/Algebra/TrivSqZeroExt.lean @@ -1051,11 +1051,7 @@ def liftEquivOfComm : toFun := fun f => ⟨(Algebra.ofId _ _, f.val), f.prop, fun r x => by simp [Algebra.smul_def, Algebra.ofId_apply], fun r x => by simp [Algebra.smul_def, Algebra.ofId_apply, Algebra.commutes]⟩ - invFun := fun fg => ⟨fg.val.2, fg.prop.1⟩ - left_inv := fun f => rfl - right_inv := fun fg => Subtype.ext <| - Prod.ext (AlgHom.toLinearMap_injective <| LinearMap.ext_ring <| by simp) - rfl } + invFun := fun fg => ⟨fg.val.2, fg.prop.1⟩ } section map diff --git a/Mathlib/Algebra/Vertex/VertexOperator.lean b/Mathlib/Algebra/Vertex/VertexOperator.lean index d2c51e5a1cdf2a..10aaf2c5432cda 100644 --- a/Mathlib/Algebra/Vertex/VertexOperator.lean +++ b/Mathlib/Algebra/Vertex/VertexOperator.lean @@ -22,7 +22,7 @@ In this file we introduce vertex operators as linear maps to Laurent series. ## References * [G. Mason, *Vertex rings and Pierce bundles*][mason2017] * [A. Matsuo, K. Nagatomo, *On axioms for a vertex algebra and locality of quantum - fields*][matsuo1997] + fields*][matsuo1997] -/ noncomputable section diff --git a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean index 4c50fc2d9aecc5..f5ba1f95aadd75 100644 --- a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean +++ b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean @@ -3,8 +3,8 @@ Copyright (c) 2025 Andrew Yang. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Andrew Yang -/ -import Mathlib.AlgebraicGeometry.Morphisms.Affine -import Mathlib.AlgebraicGeometry.Properties +import Mathlib.AlgebraicGeometry.IdealSheaf.Functorial +import Mathlib.AlgebraicGeometry.Morphisms.Separated import Mathlib.CategoryTheory.Filtered.Final /-! @@ -85,3 +85,77 @@ lemma Scheme.nonempty_of_isLimit [IsCofilteredOrEmpty I] (whiskerLeft (Over.post D) (Over.mapPullbackAdj (𝒰.map j)).counit) (Over.forget _) exact this.map (((Functor.Initial.isLimitWhiskerEquiv (Over.forget i) c).symm hc).lift ((Cones.postcompose α).obj c'.1)).base + +include hc in +open Scheme.IdealSheafData in +/-- +Suppose we have a cofiltered diagram of schemes whose transition maps are affine. The limit of +a family of compatible nonempty quasicompact closed sets in the diagram is also nonempty. +-/ +lemma exists_mem_of_isClosed_of_nonempty + [IsCofilteredOrEmpty I] + [∀ {i j} (f : i ⟶ j), IsAffineHom (D.map f)] + (Z : ∀ (i : I), Set (D.obj i)) + (hZc : ∀ (i : I), IsClosed (Z i)) + (hZne : ∀ i, (Z i).Nonempty) + (hZcpt : ∀ i, IsCompact (Z i)) + (hmapsTo : ∀ {i i' : I} (f : i ⟶ i'), Set.MapsTo (D.map f).base (Z i) (Z i')) : + ∃ (s : c.pt), ∀ i, (c.π.app i).base s ∈ Z i := by + let D' : I ⥤ Scheme := + { obj i := (vanishingIdeal ⟨Z i, hZc i⟩).subscheme + map {X Y} f := subschemeMap _ _ (D.map f) (by + rw [map_vanishingIdeal, ← le_support_iff_le_vanishingIdeal] + simpa [(hZc _).closure_subset_iff] using (hmapsTo f).subset_preimage) + map_id _ := by simp [← cancel_mono (subschemeι _)] + map_comp _ _ := by simp [← cancel_mono (subschemeι _)] } + let ι : D' ⟶ D := { app i := subschemeι _, naturality _ _ _ := by simp [D'] } + haveI {i j} (f : i ⟶ j) : IsAffineHom (D'.map f) := by + suffices IsAffineHom (D'.map f ≫ ι.app j) from .of_comp _ (ι.app j) + simp only [TopologicalSpace.Closeds.coe_mk, id_eq, TopologicalSpace.Closeds.coe_closure, + coe_support_vanishingIdeal, eq_mpr_eq_cast, subschemeMap_subschemeι, D', ι] + infer_instance + haveI (i) : Nonempty (D'.obj i) := Set.nonempty_coe_sort.mpr (hZne i) + haveI (i) : CompactSpace (D'.obj i) := isCompact_iff_compactSpace.mp (hZcpt i) + let c' : Cone D' := + { pt := (⨆ i, (vanishingIdeal ⟨Z i, hZc i⟩).comap (c.π.app i)).subscheme + π := + { app i := subschemeMap _ _ (c.π.app i) (by simp [le_map_iff_comap_le, le_iSup_of_le i]) + naturality {i j} f := by simp [D', ← cancel_mono (subschemeι _)] } } + let hc' : IsLimit c' := + { lift s := IsClosedImmersion.lift (subschemeι _) (hc.lift ((Cones.postcompose ι).obj s)) (by + suffices ∀ i, vanishingIdeal ⟨Z i, hZc i⟩ ≤ (s.π.app i ≫ ι.app i).ker by + simpa [← le_map_iff_comap_le, ← Scheme.Hom.ker_comp] + refine fun i ↦ .trans ?_ (Scheme.Hom.le_ker_comp _ _) + simp [ι]) + fac s i := by simp [← cancel_mono (subschemeι _), c', ι] + uniq s m hm := by + rw [← cancel_mono (subschemeι _)] + refine hc.hom_ext fun i ↦ ?_ + simp [← cancel_mono (subschemeι _), ι, c', ← hm] } + have : Nonempty (⨆ i, (vanishingIdeal ⟨Z i, hZc i⟩).comap (c.π.app i)).support := + Scheme.nonempty_of_isLimit D' c' hc' + simpa using this + +include hc in +/-- +A variant of `exists_mem_of_isClosed_of_nonempty` where the closed sets are only defined +for the objects over a given `j : I`. +-/ +@[stacks 01Z3] +lemma exists_mem_of_isClosed_of_nonempty' + [IsCofilteredOrEmpty I] + [∀ {i j} (f : i ⟶ j), IsAffineHom (D.map f)] + {j : I} + (Z : ∀ (i : I), (i ⟶ j) → Set (D.obj i)) + (hZc : ∀ i hij, IsClosed (Z i hij)) + (hZne : ∀ i hij, (Z i hij).Nonempty) + (hZcpt : ∀ i hij, IsCompact (Z i hij)) + (hstab : ∀ (i i' : I) (hi'i : i' ⟶ i) (hij : i ⟶ j) , + Set.MapsTo (D.map hi'i).base (Z i' (hi'i ≫ hij)) (Z i hij)) : + ∃ (s : c.pt), ∀ i hij, (c.π.app i).base s ∈ Z i hij := by + have {i₁ i₂ : Over j} (f : i₁ ⟶ i₂) : IsAffineHom ((Over.forget j ⋙ D).map f) := by + dsimp; infer_instance + simpa [Over.forall_iff] using exists_mem_of_isClosed_of_nonempty (Over.forget j ⋙ D) _ + ((Functor.Initial.isLimitWhiskerEquiv (Over.forget j) c).symm hc) + (fun i ↦ Z i.left i.hom) (fun _ ↦ hZc _ _) (fun _ ↦ hZne _ _) (fun _ ↦ hZcpt _ _) + (fun {i₁ i₂} f ↦ by dsimp; rw [← Over.w f]; exact hstab ..) diff --git a/Mathlib/AlgebraicGeometry/Cover/Sigma.lean b/Mathlib/AlgebraicGeometry/Cover/Sigma.lean index 2aeafc715b69cf..5c8800d909c74b 100644 --- a/Mathlib/AlgebraicGeometry/Cover/Sigma.lean +++ b/Mathlib/AlgebraicGeometry/Cover/Sigma.lean @@ -52,10 +52,11 @@ noncomputable def Hom.sigma (f : 𝒰 ⟶ 𝒱) : 𝒰.sigma ⟶ 𝒱.sigma wher w _ := Sigma.hom_ext _ _ (by simp) app_prop _ := by simp only [sigma_obj, sigma_J, PUnit.default_eq_unit, - IsLocalAtSource.iff_of_openCover (sigmaOpenCover _), sigmaOpenCover_obj, sigmaOpenCover_map, - colimit.ι_desc, Cofan.mk_pt, Cofan.mk_ι_app] + IsLocalAtSource.iff_of_openCover (Scheme.IsLocallyDirected.openCover _), + Discrete.functor_obj_eq_as, IsLocallyDirected.openCover_J, IsLocallyDirected.openCover_obj, + IsLocallyDirected.openCover_map, colimit.ι_desc, Cofan.mk_pt, Cofan.mk_ι_app] intro i - exact P.comp_mem _ _ (f.app_prop i) (IsLocalAtSource.of_isOpenImmersion _) + exact P.comp_mem _ _ (f.app_prop i.1) (IsLocalAtSource.of_isOpenImmersion _) /-- Collapsing a cover to a single object cover is functorial. -/ @[simps] diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Basic.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Basic.lean index 0e65cd7755c94b..9a81e1e394267c 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Basic.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Basic.lean @@ -27,7 +27,7 @@ formulae for group operations in `Mathlib/AlgebraicGeometry/EllipticCurve/Affine ## Main statements * `WeierstrassCurve.Affine.equation_iff_nonsingular`: an elliptic curve in affine coordinates is - nonsingular at every point. + nonsingular at every point. ## Implementation notes diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean index f10dab319665af..05f394ff7128f4 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean @@ -11,22 +11,22 @@ import Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic Let `W` be a Weierstrass curve over a field `F` with coefficients `aᵢ`. The nonsingular affine points on `W` can be given negation and addition operations defined by a secant-and-tangent process. * Given a nonsingular affine point `P`, its *negation* `-P` is defined to be the unique third - nonsingular point of intersection between `W` and the vertical line through `P`. - Explicitly, if `P` is `(x, y)`, then `-P` is `(x, -y - a₁x - a₃)`. + nonsingular point of intersection between `W` and the vertical line through `P`. + Explicitly, if `P` is `(x, y)`, then `-P` is `(x, -y - a₁x - a₃)`. * Given two nonsingular affine points `P` and `Q`, their *addition* `P + Q` is defined to be the - negation of the unique third nonsingular point of intersection between `W` and the line `L` - through `P` and `Q`. Explicitly, let `P` be `(x₁, y₁)` and let `Q` be `(x₂, y₂)`. - * If `x₁ = x₂` and `y₁ = -y₂ - a₁x₂ - a₃`, then `L` is vertical. - * If `x₁ = x₂` and `y₁ ≠ -y₂ - a₁x₂ - a₃`, then `L` is the tangent of `W` at `P = Q`, and has - slope `ℓ := (3x₁² + 2a₂x₁ + a₄ - a₁y₁) / (2y₁ + a₁x₁ + a₃)`. - * Otherwise `x₁ ≠ x₂`, then `L` is the secant of `W` through `P` and `Q`, and has slope - `ℓ := (y₁ - y₂) / (x₁ - x₂)`. - - In the last two cases, the `X`-coordinate of `P + Q` is then the unique third solution of the - equation obtained by substituting the line `Y = ℓ(X - x₁) + y₁` into the Weierstrass equation, - and can be written down explicitly as `x := ℓ² + a₁ℓ - a₂ - x₁ - x₂` by inspecting the - coefficients of `X²`. The `Y`-coordinate of `P + Q`, after applying the final negation that maps - `Y` to `-Y - a₁X - a₃`, is precisely `y := -(ℓ(x - x₁) + y₁) - a₁x - a₃`. + negation of the unique third nonsingular point of intersection between `W` and the line `L` + through `P` and `Q`. Explicitly, let `P` be `(x₁, y₁)` and let `Q` be `(x₂, y₂)`. + * If `x₁ = x₂` and `y₁ = -y₂ - a₁x₂ - a₃`, then `L` is vertical. + * If `x₁ = x₂` and `y₁ ≠ -y₂ - a₁x₂ - a₃`, then `L` is the tangent of `W` at `P = Q`, and has + slope `ℓ := (3x₁² + 2a₂x₁ + a₄ - a₁y₁) / (2y₁ + a₁x₁ + a₃)`. + * Otherwise `x₁ ≠ x₂`, then `L` is the secant of `W` through `P` and `Q`, and has slope + `ℓ := (y₁ - y₂) / (x₁ - x₂)`. + + In the last two cases, the `X`-coordinate of `P + Q` is then the unique third solution of the + equation obtained by substituting the line `Y = ℓ(X - x₁) + y₁` into the Weierstrass equation, + and can be written down explicitly as `x := ℓ² + a₁ℓ - a₂ - x₁ - x₂` by inspecting the + coefficients of `X²`. The `Y`-coordinate of `P + Q`, after applying the final negation that maps + `Y` to `-Y - a₁X - a₃`, is precisely `y := -(ℓ(x - x₁) + y₁) - a₁x - a₃`. This file defines polynomials associated to negation and addition of nonsingular affine points, including slopes of non-vertical lines. The actual group law on nonsingular points in affine diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean index 2e759135a151b3..777590c1bc884d 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean @@ -42,11 +42,11 @@ This file defines the group law on nonsingular points `W⟮F⟯` in affine coord ## Main statements * `WeierstrassCurve.Affine.CoordinateRing.instIsDomainCoordinateRing`: the affine coordinate ring - of a Weierstrass curve is an integral domain. + of a Weierstrass curve is an integral domain. * `WeierstrassCurve.Affine.CoordinateRing.degree_norm_smul_basis`: the degree of the norm of an - element in the affine coordinate ring in terms of its power basis. + element in the affine coordinate ring in terms of its power basis. * `WeierstrassCurve.Affine.Point.instAddCommGroup`: the type of nonsingular points `W⟮F⟯` in affine - coordinates forms an abelian group under addition. + coordinates forms an abelian group under addition. ## Notations diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Jacobian/Point.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Jacobian/Point.lean index 83a62289001669..8689bde26eebd4 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Jacobian/Point.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Jacobian/Point.lean @@ -26,14 +26,14 @@ This file defines the group law on nonsingular Jacobian points. * `WeierstrassCurve.Jacobian.Point.neg`: the negation of a nonsingular Jacobian point. * `WeierstrassCurve.Jacobian.Point.add`: the addition of two nonsingular Jacobian points. * `WeierstrassCurve.Jacobian.Point.toAffineAddEquiv`: the equivalence between the type of - nonsingular Jacobian points with the type of nonsingular points `W⟮F⟯` in affine coordinates. + nonsingular Jacobian points with the type of nonsingular points `W⟮F⟯` in affine coordinates. ## Main statements * `WeierstrassCurve.Jacobian.nonsingular_neg`: negation preserves the nonsingular condition. * `WeierstrassCurve.Jacobian.nonsingular_add`: addition preserves the nonsingular condition. * `WeierstrassCurve.Jacobian.Point.instAddCommGroup`: the type of nonsingular Jacobian points forms - an abelian group under addition. + an abelian group under addition. ## Implementation notes diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Projective/Point.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Projective/Point.lean index e9c642d6fa3a8d..3828cd6840ec12 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Projective/Point.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Projective/Point.lean @@ -26,14 +26,14 @@ This file defines the group law on nonsingular projective points. * `WeierstrassCurve.Projective.Point.neg`: the negation of a nonsingular projective point. * `WeierstrassCurve.Projective.Point.add`: the addition of two nonsingular projective points. * `WeierstrassCurve.Projective.Point.toAffineAddEquiv`: the equivalence between the type of - nonsingular projective points with the type of nonsingular points `W⟮F⟯` in affine coordinates. + nonsingular projective points with the type of nonsingular points `W⟮F⟯` in affine coordinates. ## Main statements * `WeierstrassCurve.Projective.nonsingular_neg`: negation preserves the nonsingular condition. * `WeierstrassCurve.Projective.nonsingular_add`: addition preserves the nonsingular condition. * `WeierstrassCurve.Projective.Point.instAddCommGroup`: the type of nonsingular projective points - forms an abelian group under addition. + forms an abelian group under addition. ## Implementation notes diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/VariableChange.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/VariableChange.lean index 4bb7d3dff5a907..11cf8b7a3a17f4 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/VariableChange.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/VariableChange.lean @@ -20,7 +20,7 @@ This file defines admissible linear change of variables of Weierstrass curves. * An instance which states that change of variables preserves elliptic curves. * `WeierstrassCurve.variableChange_j`: the j-invariant of an elliptic curve is invariant under an - admissible linear change of variables. + admissible linear change of variables. ## References diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Weierstrass.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Weierstrass.lean index 40299235e00e40..bb562241b536d1 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Weierstrass.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Weierstrass.lean @@ -40,7 +40,7 @@ field of `R` are precisely the `X`-coordinates of the non-zero 2-torsion points ## Main statements * `WeierstrassCurve.twoTorsionPolynomial_disc`: the discriminant of a Weierstrass curve is a - constant factor of the cubic discriminant of its 2-torsion polynomial. + constant factor of the cubic discriminant of its 2-torsion polynomial. ## Implementation notes diff --git a/Mathlib/AlgebraicGeometry/Gluing.lean b/Mathlib/AlgebraicGeometry/Gluing.lean index c71d0d30b3644e..642b1941df85f8 100644 --- a/Mathlib/AlgebraicGeometry/Gluing.lean +++ b/Mathlib/AlgebraicGeometry/Gluing.lean @@ -3,13 +3,16 @@ Copyright (c) 2022 Andrew Yang. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Andrew Yang -/ +import Mathlib.AlgebraicGeometry.Restrict +import Mathlib.CategoryTheory.LocallyDirected import Mathlib.Geometry.RingedSpace.PresheafedSpace.Gluing -import Mathlib.AlgebraicGeometry.Cover.Open /-! # Gluing Schemes Given a family of gluing data of schemes, we may glue them together. +Also see the section about "locally directed" gluing, +which is a special case where the conditions are easier to check. ## Main definitions @@ -138,6 +141,9 @@ instance : PreservesColimit (𝖣.diagram.multispan) forgetToTop := inferInstanceAs (PreservesColimit (𝖣.diagram).multispan (forgetToLocallyRingedSpace ⋙ LocallyRingedSpace.forgetToSheafedSpace ⋙ SheafedSpace.forget CommRingCat)) +instance : PreservesColimit (𝖣.diagram.multispan) forget := + inferInstanceAs (PreservesColimit (𝖣.diagram).multispan (forgetToTop ⋙ CategoryTheory.forget _)) + instance : HasMulticoequalizer 𝖣.diagram := hasColimit_of_created _ forgetToLocallyRingedSpace @@ -164,7 +170,7 @@ instance ι_isOpenImmersion (i : D.J) : IsOpenImmersion (𝖣.ι i) := by theorem ι_jointly_surjective (x : 𝖣.glued.carrier) : ∃ (i : D.J) (y : (D.U i).carrier), (D.ι i).base y = x := - 𝖣.ι_jointly_surjective (forgetToTop ⋙ forget TopCat) x + 𝖣.ι_jointly_surjective forget x /-- Promoted to higher priority to short circuit simplifier. -/ @[simp (high), reassoc] @@ -208,8 +214,9 @@ theorem ι_isoCarrier_inv (i : D.J) : (D_).ι i ≫ D.isoCarrier.inv = (D.ι i).base := by delta isoCarrier rw [Iso.trans_inv, GlueData.ι_gluedIso_inv_assoc, Functor.mapIso_inv, Iso.trans_inv, - Functor.mapIso_inv, Iso.trans_inv, SheafedSpace.forgetToPresheafedSpace_map, forget_map, - forget_map, ← PresheafedSpace.comp_base, ← Category.assoc, + Functor.mapIso_inv, Iso.trans_inv, SheafedSpace.forgetToPresheafedSpace_map, + PresheafedSpace.forget_map, + PresheafedSpace.forget_map, ← PresheafedSpace.comp_base, ← Category.assoc, D.toLocallyRingedSpaceGlueData.toSheafedSpaceGlueData.ι_isoPresheafedSpace_inv i] erw [← Category.assoc, D.toLocallyRingedSpaceGlueData.ι_isoSheafedSpace_inv i] change (_ ≫ D.isoLocallyRingedSpace.inv).base = _ @@ -441,6 +448,356 @@ theorem ι_glueMorphisms (𝒰 : OpenCover.{v} X) {Y : Scheme} (f : ∀ x, 𝒰. end Cover +lemma hom_ext_of_forall {X Y : Scheme} (f g : X ⟶ Y) + (H : ∀ x : X, ∃ U : X.Opens, x ∈ U ∧ U.ι ≫ f = U.ι ≫ g) : f = g := by + choose U hxU hU using H + let 𝒰 : X.OpenCover := { J := X, obj i := (U i), map i := (U i).ι, f x := x, covers := by simpa } + exact 𝒰.hom_ext _ _ hU + +/-! + +## Locally directed gluing + +We say that a diagram of open immersions is "locally directed" if for any `V, W ⊆ U` in the diagram, +`V ∩ W` is a union of elements in the diagram. Equivalently, for every `x ∈ U` in the diagram, +the set of elements containing `x` is directed (and hence the name). + +For such a diagram, we can glue them directly since the gluing conditions are always satisfied. +The intended usage is to provide the following instances: +- `∀ {i j} (f : i ⟶ j), IsOpenImmersion (F.map f)` +- `(F ⋙ forget).IsLocallyDirected` +and to directly use the `colimit` API. +Also see `AlgebraicGeometry.Scheme.IsLocallyDirected.openCover` for the open cover of the `colimit`. + +-/ +section IsLocallyDirected + +open TopologicalSpace.Opens + +universe w + +variable {J : Type w} [Category.{v} J] (F : J ⥤ Scheme.{u}) +variable [∀ {i j} (f : i ⟶ j), IsOpenImmersion (F.map f)] + +namespace IsLocallyDirected + +/-- (Implemetation detail) +The intersection `V` in the glue data associated to a locally directed diagram. -/ +noncomputable +def V (i j : J) : (F.obj i).Opens := ⨆ (k : Σ k, (k ⟶ i) × (k ⟶ j)), (F.map k.2.1).opensRange + +lemma V_self (i) : V F i i = ⊤ := + top_le_iff.mp (le_iSup_of_le ⟨i, 𝟙 _, 𝟙 _⟩ (by simp [Scheme.Hom.opensRange_of_isIso])) + +variable [(F ⋙ forget).IsLocallyDirected] + +lemma exists_of_pullback_V_V {i j k : J} (x : pullback (C := Scheme) (V F i j).ι (V F i k).ι) : + ∃ (l : J) (fi : l ⟶ i) (fj : l ⟶ j) (fk : l ⟶ k) + (α : F.obj l ⟶ pullback (V F i j).ι (V F i k).ι) (z : F.obj l), + IsOpenImmersion α ∧ + α ≫ pullback.fst _ _ = (F.map fi).isoOpensRange.hom ≫ + (F.obj i).homOfLE (le_iSup_of_le ⟨l, _, fj⟩ le_rfl) ∧ + α ≫ pullback.snd _ _ = (F.map fi).isoOpensRange.hom ≫ + (F.obj i).homOfLE (le_iSup_of_le ⟨l, _, fk⟩ le_rfl) ∧ + α.base z = x := by + obtain ⟨k₁, y₁, hy₁⟩ := mem_iSup.mp ((pullback.fst (C := Scheme) _ _).base x).2 + obtain ⟨k₂, y₂, hy₂⟩ := mem_iSup.mp ((pullback.snd (C := Scheme) _ _).base x).2 + obtain ⟨l, hli, hlk, z, rfl, rfl⟩ := + (F ⋙ forget).exists_map_eq_of_isLocallyDirected k₁.2.1 k₂.2.1 y₁ y₂ + (by simpa [hy₁, hy₂] using congr($(pullback.condition (f := (V F i j).ι)).base x)) + let α : F.obj l ⟶ pullback (V F i j).ι (V F i k).ι := + pullback.lift + ((F.map (hli ≫ k₁.2.1)).isoOpensRange.hom ≫ Scheme.homOfLE _ + (le_iSup_of_le ⟨l, hli ≫ k₁.2.1, hli ≫ k₁.2.2⟩ le_rfl)) + ((F.map (hli ≫ k₁.2.1)).isoOpensRange.hom ≫ Scheme.homOfLE _ + (le_iSup_of_le ⟨l, hli ≫ k₁.2.1, hlk ≫ k₂.2.2⟩ le_rfl)) + (by simp [← cancel_mono (Scheme.Opens.ι _)]) + have : IsOpenImmersion α := by + apply (config := { allowSynthFailures := true }) IsOpenImmersion.of_comp + · exact inferInstanceAs (IsOpenImmersion (pullback.fst _ _)) + · simp only [limit.lift_π, PullbackCone.mk_pt, PullbackCone.mk_π_app, α] + infer_instance + have : α.base z = x := by + apply (pullback.fst (C := Scheme) _ _).isOpenEmbedding.injective + apply (V F i j).ι.isOpenEmbedding.injective + rw [← Scheme.comp_base_apply, ← Scheme.comp_base_apply, pullback.lift_fst_assoc] + simpa using hy₁ + exact ⟨l, hli ≫ k₁.2.1, hli ≫ k₁.2.2, hlk ≫ k₂.2.2, α, z, ‹_›, by simp [α], by simp [α], ‹_›⟩ + +variable [Quiver.IsThin J] + +lemma fst_inv_eq_snd_inv + {i j : J} (k₁ k₂ : (k : J) × (k ⟶ i) × (k ⟶ j)) {U : (F.obj i).Opens} + (h₁ : (F.map k₁.2.1).opensRange ≤ U) (h₂ : (F.map k₂.2.1).opensRange ≤ U) : + pullback.fst ((F.obj i).homOfLE h₁) ((F.obj i).homOfLE h₂) ≫ + (F.map k₁.2.1).isoOpensRange.inv ≫ F.map k₁.2.2 = + pullback.snd ((F.obj i).homOfLE h₁) ((F.obj i).homOfLE h₂) ≫ + (F.map k₂.2.1).isoOpensRange.inv ≫ F.map k₂.2.2 := by + apply Scheme.hom_ext_of_forall + intro x + obtain ⟨l, hli, hlj, y, hy₁, hy₂⟩ := (F ⋙ forget).exists_map_eq_of_isLocallyDirected k₁.2.1 k₂.2.1 + ((pullback.fst _ _ ≫ (F.map k₁.2.1).isoOpensRange.inv).base x) + ((pullback.snd _ _ ≫ (F.map k₂.2.1).isoOpensRange.inv).base x) (by + simp only [Functor.comp_obj, forget_obj, Functor.comp_map, forget_map, ← comp_base_apply, + Category.assoc, Hom.isoOpensRange_inv_comp] + congr 5 + simpa using congr($(pullback.condition (f := (F.obj i).homOfLE h₁) + (g := (F.obj i).homOfLE h₂)) ≫ Scheme.Opens.ι _)) + let α : F.obj l ⟶ pullback ((F.obj i).homOfLE h₁) ((F.obj i).homOfLE h₂) := + pullback.lift + (F.map hli ≫ (F.map k₁.2.1).isoOpensRange.hom) + (F.map hlj ≫ (F.map k₂.2.1).isoOpensRange.hom) + (by simp [← cancel_mono (Scheme.Opens.ι _), ← Functor.map_comp, + Subsingleton.elim (hli ≫ k₁.2.1) (hlj ≫ k₂.2.1)]) + have : IsOpenImmersion α := by + have : IsOpenImmersion (α ≫ pullback.fst _ _) := by + simp only [pullback.lift_fst, α]; infer_instance + exact .of_comp _ (pullback.fst _ _) + have : α.base y = x := by + simp only [Functor.comp_obj, forget_obj, Functor.comp_map, forget_map, comp_coeBase, + TopCat.hom_comp, ContinuousMap.comp_apply, α] at hy₁ + apply (pullback.fst ((F.obj i).homOfLE h₁) _).isOpenEmbedding.injective + simp only [← Scheme.comp_base_apply, Category.assoc, α, pullback.lift_fst] + simp [hy₁] + refine ⟨α.opensRange, ⟨y, this⟩, ?_⟩ + rw [← cancel_epi α.isoOpensRange.hom] + simp [α, ← Functor.map_comp, Subsingleton.elim (hli ≫ k₁.2.2) (hlj ≫ k₂.2.2)] + +/-- (Implemetation detail) +The inclusion map `V i j ⟶ F j` in the glue data associated to a locally directed diagram. -/ +def tAux (i j : J) : (V F i j).toScheme ⟶ F.obj j := + (Scheme.Opens.iSupOpenCover _).glueMorphisms + (fun k ↦ (F.map k.2.1).isoOpensRange.inv ≫ F.map k.2.2) fun k₁ k₂ ↦ by + dsimp [Scheme.Opens.iSupOpenCover] + apply fst_inv_eq_snd_inv F + +@[reassoc] +lemma homOfLE_tAux (i j : J) {k : J} (fi : k ⟶ i) (fj : k ⟶ j) : + (F.obj i).homOfLE (le_iSup_of_le ⟨k, fi, fj⟩ le_rfl) ≫ + tAux F i j = (F.map fi).isoOpensRange.inv ≫ F.map fj := + (Scheme.Opens.iSupOpenCover (J := Σ k, (k ⟶ i) × (k ⟶ j)) _).ι_glueMorphisms _ _ ⟨k, fi, fj⟩ + +/-- (Implemetation detail) +The transition map `V i j ⟶ V j i` in the glue data associated to a locally directed diagram. -/ +def t (i j : J) : (V F i j).toScheme ⟶ (V F j i).toScheme := + IsOpenImmersion.lift (V F j i).ι (tAux F i j) (by + rintro _ ⟨x, rfl⟩ + obtain ⟨l, x, rfl⟩ := (Scheme.Opens.iSupOpenCover _).exists_eq x + simp only [V, tAux, ← Scheme.comp_base_apply, Category.assoc, Cover.ι_glueMorphisms] + simp only [Opens.range_ι, iSup_mk, carrier_eq_coe, Hom.coe_opensRange, coe_mk, comp_coeBase, + TopCat.hom_comp, ContinuousMap.comp_apply] + exact Set.mem_iUnion.mpr ⟨⟨l.1, l.2.2, l.2.1⟩, ⟨_, rfl⟩⟩) + +lemma t_id (i : J) : t F i i = 𝟙 _ := by + refine (Scheme.Opens.iSupOpenCover _).hom_ext _ _ fun k ↦ ?_ + simp only [Category.comp_id, ← cancel_mono (Scheme.Opens.ι _), Category.assoc, + IsOpenImmersion.lift_fac, Scheme.Cover.ι_glueMorphisms, t, tAux, V] + simp [Scheme.Opens.iSupOpenCover, Iso.inv_comp_eq, Subsingleton.elim k.2.1 k.2.2] + +variable [Small.{u} J] + +local notation3:max "↓"j:arg => Equiv.symm (equivShrink _) j + +/-- (Implemetation detail) +The glue data associated to a locally directed diagram. + +One usually does not want to use this directly, and instead use the generic `colimit` API. +-/ +def glueData : Scheme.GlueData where + J := Shrink.{u} J + U j := F.obj ↓j + V ij := V F ↓ij.1 ↓ij.2 + f i j := Scheme.Opens.ι _ + f_id i := V_self F ↓i ▸ (Scheme.topIso _).isIso_hom + f_hasPullback := inferInstance + f_open := inferInstance + t i j := t F ↓i ↓j + t_id i := t_id F ↓i + t' i j k := pullback.lift + (IsOpenImmersion.lift (V F ↓j ↓k).ι (pullback.fst _ _ ≫ tAux F ↓i ↓j) (by + rintro _ ⟨x, rfl⟩ + obtain ⟨l, fi, fj, fk, α, z, hα, hα₁, hα₂, rfl⟩ := exists_of_pullback_V_V F x + rw [← Scheme.comp_base_apply, reassoc_of% hα₁, homOfLE_tAux F ↓i ↓j fi fj, + Iso.hom_inv_id_assoc, Scheme.Opens.range_ι, SetLike.mem_coe] + exact TopologicalSpace.Opens.mem_iSup.mpr ⟨⟨l, fj, fk⟩, ⟨z, rfl⟩⟩)) + (pullback.fst _ _ ≫ t F _ _) (by simp [t]) + t_fac i j k := pullback.lift_snd _ _ _ + cocycle i j k := by + refine Scheme.hom_ext_of_forall _ _ fun x ↦ ?_ + have := exists_of_pullback_V_V F x + obtain ⟨l, fi, fj, fk, α, z, hα, hα₁, hα₂, e⟩ := this -- doing them in the same step times out. + refine ⟨α.opensRange, ⟨_, e⟩, ?_⟩ + rw [← cancel_mono (pullback.snd _ _), ← cancel_mono (Scheme.Opens.ι _)] + simp only [t, Category.assoc, limit.lift_π, Scheme.comp_coeBase, TopCat.hom_comp, + ContinuousMap.coe_comp, id_eq, PullbackCone.mk_pt, PullbackCone.mk_π_app, limit.lift_π_assoc, + cospan_left, IsOpenImmersion.lift_fac, Category.id_comp] + rw [IsOpenImmersion.comp_lift_assoc] + simp only [limit.lift_π_assoc, Scheme.comp_coeBase, TopCat.hom_comp, ContinuousMap.coe_comp, + id_eq, PullbackCone.mk_pt, cospan_left, PullbackCone.mk_π_app] + rw [← cancel_epi α.isoOpensRange.hom] + simp_rw [Scheme.Hom.isoOpensRange_hom_ι_assoc, IsOpenImmersion.comp_lift_assoc] + simp only [reassoc_of% hα₁, homOfLE_tAux F _ _ fi fj, Iso.hom_inv_id_assoc, reassoc_of% hα₂] + generalize_proofs _ h₁ + have : IsOpenImmersion.lift (V F ↓j ↓k).ι (F.map fj) h₁ = (F.map fj).isoOpensRange.hom ≫ + (F.obj ↓j).homOfLE (le_iSup_of_le ⟨l, fj, fk⟩ le_rfl) := by + rw [← cancel_mono (Scheme.Opens.ι _), Category.assoc, IsOpenImmersion.lift_fac, + ← Iso.inv_comp_eq, Scheme.Hom.isoOpensRange_inv_comp] + exact (Scheme.homOfLE_ι _ _).symm + simp_rw [this, Category.assoc, homOfLE_tAux F _ _ fj fk, Iso.hom_inv_id_assoc] + generalize_proofs h₂ + have : IsOpenImmersion.lift (V F ↓k ↓i).ι (F.map fk) h₂ = (F.map fk).isoOpensRange.hom ≫ + (F.obj ↓k).homOfLE (le_iSup_of_le ⟨l, fk, fi⟩ le_rfl) := by + rw [← cancel_mono (Scheme.Opens.ι _), Category.assoc, IsOpenImmersion.lift_fac, + ← Iso.inv_comp_eq, Scheme.Hom.isoOpensRange_inv_comp] + exact (Scheme.homOfLE_ι _ _).symm + simp_rw [this, Category.assoc, homOfLE_tAux F _ _ fk fi, Iso.hom_inv_id_assoc, + ← Iso.inv_comp_eq, Scheme.Hom.isoOpensRange_inv_comp] + exact (Scheme.homOfLE_ι _ _).symm + +lemma glueDataι_naturality {i j : Shrink.{u} J} (f : ↓i ⟶ ↓j) : + F.map f ≫ (glueData F).ι j = (glueData F).ι i := by + have : IsIso (V F ↓i ↓j).ι := by + have : V F ↓i ↓j = ⊤ := + top_le_iff.mp (le_iSup_of_le ⟨_, 𝟙 i, f⟩ (by simp [Scheme.Hom.opensRange_of_isIso])) + exact this ▸ (topIso _).isIso_hom + have : t F ↓i ↓j ≫ (V F ↓j ↓i).ι ≫ _ = (V F ↓i ↓j).ι ≫ _ := + (glueData F).glue_condition i j + simp only [t, IsOpenImmersion.lift_fac_assoc] at this + rw [← cancel_epi (V F ↓i ↓j).ι, ← this, ← Category.assoc, + ← (Iso.eq_inv_comp _).mp (homOfLE_tAux F ↓i ↓j (𝟙 i) f), + ← Category.assoc, ← Category.assoc, Category.assoc] + convert Category.id_comp _ + rw [← cancel_mono (Opens.ι _)] + simp [V, InducedCategory.category, Shrink.instCategoryShrink] + +/-- (Implemetation detail) +The cocone associated to a locally directed diagram. + +One usually does not want to use this directly, and instead use the generic `colimit` API. +-/ +def cocone : Cocone F where + pt := (glueData F).glued + ι.app j := F.map (eqToHom (by simp)) ≫ (glueData F).ι (equivShrink _ j) + ι.naturality {i j} f := by + simp only [Category.assoc, ← IsIso.inv_comp_eq, ← Functor.map_inv, ← Functor.map_comp_assoc, + glueDataι_naturality, Functor.const_obj_obj, Functor.const_obj_map, Category.comp_id] + +/-- (Implemetation detail) +The cocone associated to a locally directed diagram is a colimit. + +One usually does not want to use this directly, and instead use the generic `colimit` API. +-/ +noncomputable +def isColimit : IsColimit (cocone F) where + desc s := Multicoequalizer.desc _ _ (fun i ↦ s.ι.app ↓i) (by + rintro ⟨i, j⟩ + dsimp [glueData, GlueData.diagram] + simp only [t, IsOpenImmersion.lift_fac] + apply (Scheme.Opens.iSupOpenCover _).hom_ext _ _ fun k ↦ ?_ + simp only [Opens.iSupOpenCover, V, Scheme.homOfLE_ι_assoc] + rw [homOfLE_tAux_assoc F ↓i ↓j k.2.1 k.2.2, Iso.eq_inv_comp] + simp) + fac s j := by + refine (Category.assoc _ _ _).trans ?_ + conv_lhs => enter [2]; tactic => exact Multicoequalizer.π_desc _ _ _ _ _ + simp + uniq s m hm := Multicoequalizer.hom_ext _ _ _ fun i ↦ by + simp [← hm ↓i, cocone, reassoc_of% glueDataι_naturality] + rfl + +/-- (Implemetation detail) +The cocone associated to a locally directed diagram is a colimit as locally ringed spaces. + +One usually does not want to use this directly, and instead use the generic `colimit` API. +-/ +noncomputable +def isColimitForgetToLocallyRingedSpace : + IsColimit (Scheme.forgetToLocallyRingedSpace.mapCocone (cocone F)) where + desc s := (glueData F).isoLocallyRingedSpace.hom ≫ + Multicoequalizer.desc _ _ (fun i ↦ s.ι.app ↓i) (by + rintro ⟨i, j⟩ + dsimp [glueData, GlueData.diagram] + simp only [t, IsOpenImmersion.lift_fac, ← Category.assoc, ← Scheme.comp_toLRSHom] + rw [← cancel_epi (Scheme.Opens.iSupOpenCover _).ulift.fromGlued.toLRSHom, + ← cancel_epi (Scheme.Opens.iSupOpenCover _).ulift.gluedCover.isoLocallyRingedSpace.inv] + refine Multicoequalizer.hom_ext _ _ _ fun ⟨k, hk⟩ ↦ ?_ + rw [← CategoryTheory.GlueData.ι, reassoc_of% GlueData.ι_isoLocallyRingedSpace_inv, + reassoc_of% GlueData.ι_isoLocallyRingedSpace_inv, + ← cancel_epi (Hom.isoOpensRange (F.map _)).hom.toLRSHom] + simp only [Opens.iSupOpenCover, Cover.ulift, V, ← comp_toLRSHom_assoc, + Cover.ι_fromGlued_assoc, homOfLE_ι, Hom.isoOpensRange_hom_ι] + generalize_proofs _ h + rw [homOfLE_tAux F ↓i ↓j h.choose.2.1 h.choose.2.2, Iso.hom_inv_id_assoc] + exact (s.w h.choose.2.1).trans (s.w h.choose.2.2).symm) + fac s j := by + simp only [cocone, Functor.mapCocone_ι_app, Scheme.comp_toLRSHom, + forgetToLocallyRingedSpace_map, ← GlueData.ι_isoLocallyRingedSpace_inv] + simpa [CategoryTheory.GlueData.ι] using s.w _ + uniq s m hm := by + rw [← Iso.inv_comp_eq] + refine Multicoequalizer.hom_ext _ _ _ fun i ↦ ?_ + conv_lhs => rw [← ι.eq_def] + dsimp + simp [cocone, ← hm, glueDataι_naturality, + ← GlueData.ι_isoLocallyRingedSpace_inv, -ι_gluedIso_inv_assoc, -ι_gluedIso_inv] + +instance : HasColimit F := ⟨_, isColimit F⟩ + +instance : PreservesColimit F Scheme.forgetToLocallyRingedSpace := + preservesColimit_of_preserves_colimit_cocone (isColimit F) (isColimitForgetToLocallyRingedSpace F) + +instance : CreatesColimit F Scheme.forgetToLocallyRingedSpace := + CategoryTheory.createsColimitOfReflectsIsomorphismsOfPreserves + +/-- The open cover of the colimit of a locally directed diagram by the components. -/ +def openCover : (colimit F).OpenCover := + ((coverOfIsIso ((isColimit F).coconePointUniqueUpToIso (colimit.isColimit F)).hom).bind + fun i ↦ (glueData F).openCover).copy J F.obj (colimit.ι F) + ((equivShrink J).trans <| (Equiv.uniqueSigma fun (_ : Unit) ↦ Shrink J).symm) + (fun _ ↦ F.mapIso (eqToIso (by simp [GlueData.openCover, glueData]))) fun i ↦ by + show colimit.ι F i = _ ≫ (glueData F).ι (equivShrink J i) ≫ _ + simp [← Category.assoc, ← Iso.comp_inv_eq, cocone] + +@[simp] lemma openCover_J : (openCover F).J = J := rfl +@[simp] lemma openCover_obj : (openCover F).obj = F.obj := rfl +@[simp] lemma openCover_map : (openCover F).map = colimit.ι F := rfl + +instance (i) : IsOpenImmersion (colimit.ι F i) := + inferInstanceAs (IsOpenImmersion ((openCover F).map i)) + +lemma ι_eq_ι_iff {i j : J} {xi : F.obj i} {xj : F.obj j} : + (colimit.ι F i).base xi = (colimit.ι F j).base xj ↔ + ∃ k fi fj, ∃ (x : F.obj k), (F.map fi).base x = xi ∧ (F.map fj).base x = xj := by + constructor; swap + · rintro ⟨k, fi, fj, x, rfl, rfl⟩; simp only [← Scheme.comp_base_apply, colimit.w] + obtain ⟨i, rfl⟩ := (equivShrink J).symm.surjective i + obtain ⟨j, rfl⟩ := (equivShrink J).symm.surjective j + rw [← ((isColimit F).coconePointUniqueUpToIso + (colimit.isColimit F)).inv.isOpenEmbedding.injective.eq_iff] + simp only [Limits.colimit, ← Scheme.comp_base_apply, + colimit.comp_coconePointUniqueUpToIso_inv, cocone, glueDataι_naturality] + refine ?_ ∘ ((glueData F).ι_eq_iff _ _ _ _).mp + dsimp only [GlueData.Rel] + rintro ⟨x, rfl, rfl⟩ + obtain ⟨⟨k, ki, kj⟩, y, hy : (F.map ki).base y = ((glueData F).f i j).base x⟩ := mem_iSup.mp x.2 + refine ⟨k, ki, kj, y, hy, ?_⟩ + obtain ⟨k, rfl⟩ := (equivShrink J).symm.surjective k + apply ((glueData F).ι _).isOpenEmbedding.injective + simp only [← Scheme.comp_base_apply, Category.assoc, GlueData.glue_condition] + trans ((glueData F).ι k).base y + · simp [← glueDataι_naturality F kj]; rfl + · simp [← glueDataι_naturality F ki, ← hy]; rfl + +instance (F : WidePushoutShape J ⥤ Scheme.{u}) [∀ {i j} (f : i ⟶ j), IsOpenImmersion (F.map f)] : + (F ⋙ forget).IsLocallyDirected := + have (i) : Mono ((F ⋙ forget).map (.init i)) := + (mono_iff_injective _).mpr (F.map _).isOpenEmbedding.injective + inferInstance + +end IsLocallyDirected + +end IsLocallyDirected + end Scheme end AlgebraicGeometry diff --git a/Mathlib/AlgebraicGeometry/IdealSheaf/Subscheme.lean b/Mathlib/AlgebraicGeometry/IdealSheaf/Subscheme.lean index 694fe45fcdcccc..7a763ee82f46d5 100644 --- a/Mathlib/AlgebraicGeometry/IdealSheaf/Subscheme.lean +++ b/Mathlib/AlgebraicGeometry/IdealSheaf/Subscheme.lean @@ -341,9 +341,9 @@ private lemma gluedTo_injective : Function.Injective I.gluedTo.base := by intro a b e obtain ⟨ia, a : I.glueDataObj ia, rfl⟩ := - I.glueData.toGlueData.ι_jointly_surjective (Scheme.forgetToTop ⋙ forget _) a + I.glueData.toGlueData.ι_jointly_surjective forget a obtain ⟨ib, b : I.glueDataObj ib, rfl⟩ := - I.glueData.toGlueData.ι_jointly_surjective (Scheme.forgetToTop ⋙ forget _) b + I.glueData.toGlueData.ι_jointly_surjective forget b show (I.glueData.ι ia).base a = (I.glueData.ι ib).base b have : ((I.glueDataObjι ia).base a).1 = ((I.glueDataObjι ib).base b).1 := by have : (I.glueData.ι ia ≫ I.gluedTo).base a = @@ -374,7 +374,7 @@ private lemma range_gluedTo : Set.range I.gluedTo.base = I.support := by refine subset_antisymm (Set.range_subset_iff.mpr fun x ↦ ?_) ?_ · obtain ⟨ix, x : I.glueDataObj ix, rfl⟩ := - I.glueData.toGlueData.ι_jointly_surjective (Scheme.forgetToTop ⋙ forget _) x + I.glueData.toGlueData.ι_jointly_surjective forget x show (I.glueData.ι _ ≫ I.gluedTo).base x ∈ I.support rw [ι_gluedTo] exact ((I.range_glueDataObjι_ι_eq_support_inter ix).le ⟨_, rfl⟩).1 diff --git a/Mathlib/AlgebraicGeometry/Limits.lean b/Mathlib/AlgebraicGeometry/Limits.lean index c05276fe9974a0..d4662cc02a62ea 100644 --- a/Mathlib/AlgebraicGeometry/Limits.lean +++ b/Mathlib/AlgebraicGeometry/Limits.lean @@ -151,83 +151,11 @@ section Coproduct variable {ι : Type u} (f : ι → Scheme.{u}) -/-- (Implementation Detail) The glue data associated to a disjoint union. -/ -@[simps] -noncomputable -def disjointGlueData' : GlueData' Scheme where - J := ι - U := f - V _ _ _ := ∅ - f _ _ _ := Scheme.emptyTo _ - t _ _ _ := 𝟙 _ - t' _ _ _ _ _ _ := Limits.pullback.fst _ _ ≫ Scheme.emptyTo _ - t_fac _ _ _ _ _ _ := emptyIsInitial.strict_hom_ext _ _ - t_inv _ _ _ := Category.comp_id _ - cocycle _ _ _ _ _ _ := (emptyIsInitial.ofStrict (pullback.fst _ _)).hom_ext _ _ - f_mono _ _ := inferInstance - -/-- (Implementation Detail) The glue data associated to a disjoint union. -/ -@[simps! J V U f t] -noncomputable -def disjointGlueData : Scheme.GlueData where - __ := GlueData.ofGlueData' (disjointGlueData' f) - f_open i j := by - dsimp only [GlueData.ofGlueData', GlueData'.f', disjointGlueData'] - split <;> infer_instance - -/-- (Implementation Detail) The cofan in `LocallyRingedSpace` associated to a disjoint union. -/ -noncomputable -def toLocallyRingedSpaceCoproductCofan : Cofan (Scheme.toLocallyRingedSpace ∘ f) := - Cofan.mk (disjointGlueData f).toLocallyRingedSpaceGlueData.glued - (disjointGlueData f).toLocallyRingedSpaceGlueData.ι - -/-- (Implementation Detail) -The cofan in `LocallyRingedSpace` associated to a disjoint union is a colimit. -/ -noncomputable -def toLocallyRingedSpaceCoproductCofanIsColimit : - IsColimit (toLocallyRingedSpaceCoproductCofan f) := by - fapply mkCofanColimit - · refine fun t ↦ Multicoequalizer.desc _ _ t.inj ?_ - rintro ⟨i, j⟩ - simp only [GlueData.diagram, disjointGlueData_J, disjointGlueData_V, disjointGlueData_U, - disjointGlueData_f, disjointGlueData_t, Category.comp_id, Category.assoc, - GlueData.mapGlueData_J, disjointGlueData_J, GlueData.mapGlueData_V, - disjointGlueData_V, Scheme.forgetToLocallyRingedSpace_obj, GlueData.mapGlueData_U, - disjointGlueData_U, GlueData.mapGlueData_f, disjointGlueData_f, Category.comp_id, - Scheme.forgetToLocallyRingedSpace_map, GlueData.mapGlueData_t, disjointGlueData_t] - split_ifs with h - · subst h - simp only [eqToHom_refl, ↓reduceDIte, ← Category.assoc, GlueData'.f'] - congr - · apply Limits.IsInitial.hom_ext - rw [if_neg h] - exact LocallyRingedSpace.emptyIsInitial - · exact fun _ _ ↦ Multicoequalizer.π_desc _ _ _ _ _ - · intro i m h - apply Multicoequalizer.hom_ext _ _ _ fun j ↦ ?_ - rw [Multicoequalizer.π_desc] - exact h j - -noncomputable -instance : CreatesColimit (Discrete.functor f) Scheme.forgetToLocallyRingedSpace := - createsColimitOfFullyFaithfulOfIso (disjointGlueData f).gluedScheme <| - let F : Discrete.functor f ⋙ Scheme.forgetToLocallyRingedSpace ≅ - Discrete.functor (Scheme.toLocallyRingedSpace ∘ f) := Discrete.natIsoFunctor - have := (IsColimit.precomposeHomEquiv F _).symm (toLocallyRingedSpaceCoproductCofanIsColimit f) - (colimit.isoColimitCocone ⟨_, this⟩).symm - variable {σ : Type v} (g : σ → Scheme.{u}) noncomputable instance [Small.{u} σ] : - CreatesColimitsOfShape (Discrete σ) Scheme.forgetToLocallyRingedSpace.{u} := by - choose σ' eq using Small.equiv_small.{u} (α := σ) - let e : Discrete σ ≌ Discrete σ' := Discrete.equivalence eq.some - let _ : CreatesColimitsOfShape (Discrete σ') Scheme.forgetToLocallyRingedSpace := by - constructor - intro K - exact createsColimitOfIsoDiagram _ (Discrete.natIsoFunctor (F := K)).symm - apply CategoryTheory.createsColimitsOfShapeOfEquiv e.symm + CreatesColimitsOfShape (Discrete σ) Scheme.forgetToLocallyRingedSpace.{u} where instance [Small.{u} σ] : PreservesColimitsOfShape (Discrete σ) Scheme.forgetToTop.{u} := inferInstanceAs (PreservesColimitsOfShape (Discrete σ) (Scheme.forgetToLocallyRingedSpace ⋙ @@ -236,59 +164,15 @@ instance [Small.{u} σ] : PreservesColimitsOfShape (Discrete σ) Scheme.forgetTo instance [Small.{u} σ] : HasColimitsOfShape (Discrete σ) Scheme.{u} := ⟨fun _ ↦ hasColimit_of_created _ Scheme.forgetToLocallyRingedSpace⟩ -instance [UnivLE.{v, u}] : HasCoproducts.{v} Scheme.{u} := - fun _ ↦ ⟨fun _ ↦ hasColimit_of_created _ Scheme.forgetToLocallyRingedSpace⟩ - -/-- (Implementation Detail) Coproduct of schemes is isomorphic to the disjoint union. -/ -noncomputable -def sigmaIsoGlued : ∐ f ≅ (disjointGlueData f).glued := - Scheme.fullyFaithfulForgetToLocallyRingedSpace.preimageIso - (PreservesCoproduct.iso _ _ ≪≫ - colimit.isoColimitCocone ⟨_, toLocallyRingedSpaceCoproductCofanIsColimit f⟩ ≪≫ - (disjointGlueData f).isoLocallyRingedSpace.symm) - -@[reassoc (attr := simp)] -lemma ι_sigmaIsoGlued_inv (i) : (disjointGlueData f).ι i ≫ (sigmaIsoGlued f).inv = Sigma.ι f i := by - apply Scheme.forgetToLocallyRingedSpace.map_injective - dsimp [sigmaIsoGlued] - simp only [Category.assoc] - refine ((disjointGlueData f).ι_gluedIso_hom_assoc Scheme.forgetToLocallyRingedSpace i _).trans ?_ - refine (colimit.isoColimitCocone_ι_inv_assoc - ⟨_, toLocallyRingedSpaceCoproductCofanIsColimit f⟩ ⟨i⟩ _).trans ?_ - exact ι_comp_sigmaComparison Scheme.forgetToLocallyRingedSpace _ _ - -@[reassoc (attr := simp)] -lemma ι_sigmaIsoGlued_hom (i) : - Sigma.ι f i ≫ (sigmaIsoGlued f).hom = (disjointGlueData f).ι i := by - rw [← ι_sigmaIsoGlued_inv, Category.assoc, Iso.inv_hom_id, Category.comp_id] - -instance (i) : IsOpenImmersion (Sigma.ι f i) := by - rw [← ι_sigmaIsoGlued_inv] - infer_instance - -instance [Small.{u} σ] (i) : IsOpenImmersion (Sigma.ι g i) := by - obtain ⟨ι, ⟨e⟩⟩ := Small.equiv_small (α := σ) - obtain ⟨i, rfl⟩ := e.symm.surjective i - rw [← Sigma.ι_reindex_hom e.symm g i] - infer_instance - lemma sigmaι_eq_iff (i j : ι) (x y) : (Sigma.ι f i).base x = (Sigma.ι f j).base y ↔ (Sigma.mk i x : Σ i, f i) = Sigma.mk j y := by - constructor - · intro H - rw [← ι_sigmaIsoGlued_inv, ← ι_sigmaIsoGlued_inv] at H - erw [(TopCat.homeoOfIso - (Scheme.forgetToTop.mapIso (sigmaIsoGlued f))).symm.injective.eq_iff] at H - by_cases h : i = j - · subst h - simp only [Sigma.mk.inj_iff, heq_eq_eq, true_and] - exact ((disjointGlueData f).ι i).isOpenEmbedding.injective H - · obtain ⟨z, _⟩ := (Scheme.GlueData.ι_eq_iff _ _ _ _ _).mp H - · simp only [disjointGlueData_J, disjointGlueData_V, h, ↓reduceIte] at z - cases z - · rintro ⟨rfl⟩ - rfl + refine (Scheme.IsLocallyDirected.ι_eq_ι_iff _).trans ⟨?_, ?_⟩ + · rintro ⟨k, ⟨⟨⟨⟩⟩⟩, ⟨⟨⟨⟩⟩⟩, x, rfl, rfl⟩; simp + · simp only [Discrete.functor_obj_eq_as, Sigma.mk.injEq] + rintro ⟨rfl, e⟩ + obtain rfl := (heq_eq_eq x y).mp e + exact ⟨⟨i⟩, 𝟙 _, 𝟙 _, x, by simp⟩ /-- The images of each component in the coproduct is disjoint. -/ lemma disjoint_opensRange_sigmaι (i j : ι) (h : i ≠ j) : @@ -299,28 +183,9 @@ lemma disjoint_opensRange_sigmaι (i j : ι) (h : i ≠ j) : obtain ⟨rfl⟩ := (sigmaι_eq_iff _ _ _ _ _).mp hy cases h rfl -lemma exists_sigmaι_eq [Small.{u} σ] (x : (∐ g :)) : ∃ i y, (Sigma.ι g i).base y = x := by - obtain ⟨ι, ⟨e⟩⟩ := Small.equiv_small (α := σ) - let x' : (∐ g ∘ e.symm :) := (Sigma.reindex e.symm g).inv.base x - obtain ⟨i, y, h⟩ := (disjointGlueData <| g ∘ e.symm).ι_jointly_surjective - ((sigmaIsoGlued <| g ∘ e.symm).hom.base <| x') - refine ⟨e.symm i, y, (Sigma.reindex e.symm g).inv.isOpenEmbedding.injective ?_⟩ - apply (sigmaIsoGlued _).hom.isOpenEmbedding.injective - rwa [← Scheme.comp_base_apply, ← Scheme.comp_base_apply, ← Scheme.comp_base_apply, - Sigma.ι_reindex_inv_assoc, ι_sigmaIsoGlued_hom] - -lemma iSup_opensRange_sigmaι : ⨆ i, (Sigma.ι f i).opensRange = ⊤ := - eq_top_iff.mpr fun x ↦ by simpa using exists_sigmaι_eq f x - /-- The open cover of the coproduct. -/ -@[simps obj map] -noncomputable -def sigmaOpenCover [Small.{u} σ] : (∐ g).OpenCover where - J := σ - obj := g - map := Sigma.ι g - f x := (exists_sigmaι_eq g x).choose - covers x := (exists_sigmaι_eq g x).choose_spec +@[deprecated (since := "2025-06-02")] +noncomputable alias sigmaOpenCover := Scheme.IsLocallyDirected.openCover /-- The underlying topological space of the coproduct is homeomorphic to the disjoint union. -/ noncomputable @@ -360,12 +225,13 @@ private lemma isOpenImmersion_sigmaDesc_aux intro i simpa [← Scheme.comp_base_apply] using (α i).isOpenEmbedding.isOpenMap · intro x - have ⟨y, hy⟩ := (sigmaOpenCover f).covers x + have ⟨y, hy⟩ := (Scheme.IsLocallyDirected.openCover (Discrete.functor f)).covers x rw [← hy] - refine IsIso.of_isIso_fac_right (g := ((sigmaOpenCover f).map _).stalkMap y) + refine IsIso.of_isIso_fac_right + (g := ((Scheme.IsLocallyDirected.openCover (Discrete.functor f)).map _).stalkMap y) (h := (X.presheaf.stalkCongr (.of_eq ?_)).hom ≫ (α _).stalkMap _) ?_ · simp [← Scheme.comp_base_apply] - · simp [← Scheme.stalkMap_comp, Scheme.stalkMap_congr_hom _ _ (Sigma.ι_desc _ _)] + · simp [← Scheme.stalkMap_comp, Scheme.stalkMap_congr_hom _ _ (colimit.ι_desc _ _)] open scoped Function in lemma isOpenImmersion_sigmaDesc [Small.{u} σ] diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Basic.lean b/Mathlib/AlgebraicGeometry/Morphisms/Basic.lean index f45eb43f9d0163..d31eaaf29e0cbe 100644 --- a/Mathlib/AlgebraicGeometry/Morphisms/Basic.lean +++ b/Mathlib/AlgebraicGeometry/Morphisms/Basic.lean @@ -5,7 +5,6 @@ Authors: Andrew Yang -/ import Mathlib.AlgebraicGeometry.AffineScheme import Mathlib.AlgebraicGeometry.Pullbacks -import Mathlib.AlgebraicGeometry.Limits import Mathlib.CategoryTheory.MorphismProperty.Limits import Mathlib.Data.List.TFAE @@ -300,7 +299,7 @@ lemma isLocalAtTarget [P.IsMultiplicative] lemma sigmaDesc {X : Scheme.{u}} {ι : Type v} [Small.{u} ι] {Y : ι → Scheme.{u}} {f : ∀ i, Y i ⟶ X} (hf : ∀ i, P (f i)) : P (Sigma.desc f) := by - rw [IsLocalAtSource.iff_of_openCover (P := P) (sigmaOpenCover _)] + rw [IsLocalAtSource.iff_of_openCover (P := P) (Scheme.IsLocallyDirected.openCover _)] exact fun i ↦ by simp [hf] instance top : IsLocalAtSource (⊤ : MorphismProperty Scheme.{u}) where diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Separated.lean b/Mathlib/AlgebraicGeometry/Morphisms/Separated.lean index 461406237cc752..3242e5cdb861ce 100644 --- a/Mathlib/AlgebraicGeometry/Morphisms/Separated.lean +++ b/Mathlib/AlgebraicGeometry/Morphisms/Separated.lean @@ -209,9 +209,21 @@ lemma IsSeparated.of_comp [IsSeparated (f ≫ g)] : IsSeparated f := by rw [pullback.diagonal_comp] at this exact ⟨@IsClosedImmersion.of_comp _ _ _ _ _ this inferInstance⟩ +variable {f g} in lemma IsSeparated.comp_iff [IsSeparated g] : IsSeparated (f ≫ g) ↔ IsSeparated f := ⟨fun _ ↦ .of_comp f g, fun _ ↦ inferInstance⟩ +lemma IsAffineHom.of_comp [IsAffineHom (f ≫ g)] [IsSeparated g] : + IsAffineHom f := by + rw [← pullback.lift_snd (𝟙 _) f (Category.id_comp (f ≫ g))] + have := MorphismProperty.pullback_snd (P := @IsAffineHom) (f ≫ g) g inferInstance + infer_instance + +variable {f g} in +lemma IsAffineHom.comp_iff [IsAffineHom g] : + IsAffineHom (f ≫ g) ↔ IsAffineHom f := + ⟨fun _ ↦ .of_comp f g, fun _ ↦ inferInstance⟩ + @[stacks 01KM] instance isClosedImmersion_equalizer_ι_left {S : Scheme} {X Y : Over S} [IsSeparated Y.hom] (f g : X ⟶ Y) : IsClosedImmersion (equalizer.ι f g).left := by diff --git a/Mathlib/AlgebraicGeometry/OpenImmersion.lean b/Mathlib/AlgebraicGeometry/OpenImmersion.lean index e09b364f460e7c..4c47b3b07d693c 100644 --- a/Mathlib/AlgebraicGeometry/OpenImmersion.lean +++ b/Mathlib/AlgebraicGeometry/OpenImmersion.lean @@ -509,6 +509,10 @@ instance forgetToTop_preserves_of_left : PreservesLimit (cospan f g) Scheme.forg instance forgetToTop_preserves_of_right : PreservesLimit (cospan g f) Scheme.forgetToTop := preservesPullback_symmetry _ _ _ +instance : PreservesLimit (cospan f g) Scheme.forget := by delta Scheme.forget; infer_instance + +instance : PreservesLimit (cospan g f) Scheme.forget := by delta Scheme.forget; infer_instance + theorem range_pullback_snd_of_left : Set.range (pullback.snd f g).base = (g ⁻¹ᵁ f.opensRange).1 := by rw [← show _ = (pullback.snd f g).base from @@ -591,6 +595,11 @@ theorem lift_uniq (H' : Set.range g.base ⊆ Set.range f.base) (l : Y ⟶ X) (hl Scheme.Hom.ext' <| LocallyRingedSpace.IsOpenImmersion.lift_uniq f.toLRSHom g.toLRSHom H' l.toLRSHom congr(($hl).toLRSHom) +@[reassoc] +lemma comp_lift {Y' : Scheme} (g' : Y' ⟶ Y) (H : Set.range g.base ⊆ Set.range f.base) : + g' ≫ lift f g H = lift f (g' ≫ g) (.trans (by simp [Set.range_comp_subset_range]) H) := by + simp [← cancel_mono f] + theorem isPullback_lift_id {X U Y : Scheme.{u}} (f : X ⟶ Y) (g : U ⟶ Y) [IsOpenImmersion g] (H : Set.range f.base ⊆ Set.range g.base) : diff --git a/Mathlib/AlgebraicGeometry/PointsPi.lean b/Mathlib/AlgebraicGeometry/PointsPi.lean index 0e98f659ada527..8d892493dabe02 100644 --- a/Mathlib/AlgebraicGeometry/PointsPi.lean +++ b/Mathlib/AlgebraicGeometry/PointsPi.lean @@ -129,7 +129,6 @@ lemma pointsPi_surjective [CompactSpace X] [∀ i, IsLocalRing (R i)] : let e : (Π i, R i) ≃+* Π j₀, R' j₀ := { toFun f _ i := f i invFun f i := f _ ⟨i, rfl⟩ - left_inv _ := rfl right_inv _ := funext₂ fun j₀ i ↦ by rcases i with ⟨i, rfl⟩; rfl map_mul' _ _ := rfl map_add' _ _ := rfl } diff --git a/Mathlib/AlgebraicGeometry/Restrict.lean b/Mathlib/AlgebraicGeometry/Restrict.lean index afb1d69f0544a2..09b5a20787bd07 100644 --- a/Mathlib/AlgebraicGeometry/Restrict.lean +++ b/Mathlib/AlgebraicGeometry/Restrict.lean @@ -269,6 +269,15 @@ instance (X : Scheme.{u}) {U V : X.Opens} (e : U ≤ V) : IsOpenImmersion (X.hom delta Scheme.homOfLE infer_instance +/-- The open cover of `⋃ Vᵢ` by `Vᵢ`. -/ +def Scheme.Opens.iSupOpenCover {J : Type*} {X : Scheme} (U : J → X.Opens) : + (⨆ i, U i).toScheme.OpenCover where + J := J + obj i := U i + map j := X.homOfLE (le_iSup _ _) + f x := (TopologicalSpace.Opens.mem_iSup.mp x.2).choose + covers x := ⟨⟨x.1, (TopologicalSpace.Opens.mem_iSup.mp x.2).choose_spec⟩, Subtype.ext (by simp)⟩ + variable (X) in /-- The functor taking open subsets of `X` to open subschemes of `X`. -/ @[simps! obj_left obj_hom map_left] diff --git a/Mathlib/AlgebraicGeometry/Scheme.lean b/Mathlib/AlgebraicGeometry/Scheme.lean index a73a65aef420a0..059ad173cb47f1 100644 --- a/Mathlib/AlgebraicGeometry/Scheme.lean +++ b/Mathlib/AlgebraicGeometry/Scheme.lean @@ -271,6 +271,19 @@ instance hasCoeToTopCat : CoeOut Scheme TopCat where unif_hint forgetToTop_obj_eq_coe (X : Scheme) where ⊢ forgetToTop.obj X ≟ (X : TopCat) +/-- The forgetful functor from `Scheme` to `Type`. -/ +nonrec def forget : Scheme.{u} ⥤ Type u := Scheme.forgetToTop ⋙ forget TopCat + +/-- forgetful functor to `Scheme` is the same as coercion -/ +-- Schemes are often coerced as types, and it would be useful to have definitionally equal types +-- to be reducibly equal. The alternative is to make `forget` reducible but that option has +-- poor performance consequences. +unif_hint forget_obj_eq_coe (X : Scheme) where ⊢ + forget.obj X ≟ (X : Type*) + +@[simp] lemma forget_obj (X) : Scheme.forget.obj X = X := rfl +@[simp] lemma forget_map {X Y} (f : X ⟶ Y) : forget.map f = (f.base : X → Y) := rfl + @[simp] theorem id.base (X : Scheme) : (𝟙 X :).base = 𝟙 _ := rfl diff --git a/Mathlib/AlgebraicTopology/FundamentalGroupoid/Basic.lean b/Mathlib/AlgebraicTopology/FundamentalGroupoid/Basic.lean index 034db19522e71a..a547822a34656e 100644 --- a/Mathlib/AlgebraicTopology/FundamentalGroupoid/Basic.lean +++ b/Mathlib/AlgebraicTopology/FundamentalGroupoid/Basic.lean @@ -223,8 +223,6 @@ namespace FundamentalGroupoid def equiv (X : Type*) : FundamentalGroupoid X ≃ X where toFun x := x.as invFun x := .mk x - left_inv _ := rfl - right_inv _ := rfl @[simp] lemma isEmpty_iff (X : Type*) : diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/HomotopyCat.lean b/Mathlib/AlgebraicTopology/SimplicialSet/HomotopyCat.lean index 69b86d6bb6331f..4c4a4f07b2604c 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/HomotopyCat.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/HomotopyCat.lean @@ -137,7 +137,6 @@ def OneTruncation₂.nerveEquiv : toFun X := X.obj' 0 invFun X := .mk₀ X left_inv _ := ComposableArrows.ext₀ rfl - right_inv _ := rfl /-- A hom equivalence over the function `OneTruncation₂.nerveEquiv`. -/ def OneTruncation₂.nerveHomEquiv (X Y : OneTruncation₂ ((SSet.truncation 2).obj (nerve C))) : diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/Nerve.lean b/Mathlib/AlgebraicTopology/SimplicialSet/Nerve.lean index 7064ee440548d0..d99e2e38a19b60 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/Nerve.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/Nerve.lean @@ -54,7 +54,6 @@ def nerveEquiv (C : Type u) [Category.{v} C] : nerve C _⦋0⦌ ≃ C where toFun f := f.obj ⟨0, by omega⟩ invFun f := (Functor.const _).obj f left_inv f := ComposableArrows.ext₀ rfl - right_inv f := rfl namespace nerve diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean b/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean index df8040b15ac398..519f98b7c3d377 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean @@ -128,7 +128,6 @@ def obj₀Equiv {n : ℕ} : Δ[n] _⦋0⦌ ≃ Fin (n + 1) where toFun x := x 0 invFun i := const _ i _ left_inv x := by ext i : 1; fin_cases i; rfl - right_inv _ := rfl /-- The edge of the standard simplex with endpoints `a` and `b`. -/ def edge (n : ℕ) (a b : Fin (n + 1)) (hab : a ≤ b) : Δ[n] _⦋1⦌ := by diff --git a/Mathlib/Analysis/Analytic/IsolatedZeros.lean b/Mathlib/Analysis/Analytic/IsolatedZeros.lean index 0a75aaa36dd43e..699d7319226ec3 100644 --- a/Mathlib/Analysis/Analytic/IsolatedZeros.lean +++ b/Mathlib/Analysis/Analytic/IsolatedZeros.lean @@ -337,10 +337,12 @@ theorem AnalyticAt.map_nhdsNE {x : 𝕜} {f : 𝕜 → E} (hfx : AnalyticAt 𝕜 (h₂f : ¬EventuallyConst f (𝓝 x)) : (𝓝[≠] x).map f ≤ (𝓝[≠] f x) := fun _ hs ↦ mem_map.1 (preimage_of_nhdsNE hfx h₂f hs) -/-- Preimages of codiscrete sets: if `f` is analytic on a neighbourhood of `U` and not locally -constant, then the preimage of any subset codiscrete within `f '' U` is codiscrete within `U`. +/-- +Preimages of codiscrete sets: if `f` is analytic on a neighbourhood of `U` and not locally constant, +then the preimage of any subset codiscrete within `f '' U` is codiscrete within `U`. -Applications might want to use the theorem `Filter.codiscreteWithin.mono`. +See `AnalyticOnNhd.preimage_zero_codiscreteWithin` for the special case that `s` is the complement +of zero. Applications might want to use the theorem `Filter.codiscreteWithin.mono`. -/ theorem AnalyticOnNhd.preimage_mem_codiscreteWithin {U : Set 𝕜} {s : Set E} {f : 𝕜 → E} (hfU : AnalyticOnNhd 𝕜 f U) (h₂f : ∀ x ∈ U, ¬EventuallyConst f (𝓝 x)) diff --git a/Mathlib/Analysis/Analytic/Order.lean b/Mathlib/Analysis/Analytic/Order.lean index a7d51cd25b2888..cfdb0838fbe6bf 100644 --- a/Mathlib/Analysis/Analytic/Order.lean +++ b/Mathlib/Analysis/Analytic/Order.lean @@ -336,11 +336,11 @@ end NontriviallyNormedField namespace AnalyticOnNhd -variable {U : Set 𝕜} {f : 𝕜 → E} (hf : AnalyticOnNhd 𝕜 f U) -include hf +variable {U : Set 𝕜} {f : 𝕜 → E} /-- The set where an analytic function has infinite order is clopen in its domain of analyticity. -/ -theorem isClopen_setOf_analyticOrderAt_eq_top : IsClopen {u : U | analyticOrderAt f u = ⊤} := by +theorem isClopen_setOf_analyticOrderAt_eq_top (hf : AnalyticOnNhd 𝕜 f U) : + IsClopen {u : U | analyticOrderAt f u = ⊤} := by constructor · rw [← isOpen_compl_iff, isOpen_iff_forall_mem_open] intro z hz @@ -383,7 +383,7 @@ theorem isClopen_setOf_analyticOrderAt_eq_top : IsClopen {u : U | analyticOrderA /-- On a connected set, there exists a point where a meromorphic function `f` has finite order iff `f` has finite order at every point. -/ -theorem exists_analyticOrderAt_ne_top_iff_forall (hU : IsConnected U) : +theorem exists_analyticOrderAt_ne_top_iff_forall (hf : AnalyticOnNhd 𝕜 f U) (hU : IsConnected U) : (∃ u : U, analyticOrderAt f u ≠ ⊤) ↔ (∀ u : U, analyticOrderAt f u ≠ ⊤) := by have : ConnectedSpace U := Subtype.connectedSpace hU obtain ⟨v⟩ : Nonempty U := inferInstance @@ -393,22 +393,67 @@ theorem exists_analyticOrderAt_ne_top_iff_forall (hU : IsConnected U) : /-- On a preconnected set, a meromorphic function has finite order at one point if it has finite order at another point. -/ -theorem analyticOrderAt_ne_top_of_isPreconnected {x y : 𝕜} (hU : IsPreconnected U) (h₁x : x ∈ U) - (hy : y ∈ U) (h₂x : analyticOrderAt f x ≠ ⊤) : analyticOrderAt f y ≠ ⊤ := +theorem analyticOrderAt_ne_top_of_isPreconnected {x y : 𝕜} (hf : AnalyticOnNhd 𝕜 f U) + (hU : IsPreconnected U) (h₁x : x ∈ U) (hy : y ∈ U) (h₂x : analyticOrderAt f x ≠ ⊤) : + analyticOrderAt f y ≠ ⊤ := (hf.exists_analyticOrderAt_ne_top_iff_forall ⟨nonempty_of_mem h₁x, hU⟩).1 (by use ⟨x, h₁x⟩) ⟨y, hy⟩ /-- The set where an analytic function has zero or infinite order is discrete within its domain of analyticity. -/ -theorem codiscrete_setOf_analyticOrderAt_eq_zero_or_top : +theorem codiscrete_setOf_analyticOrderAt_eq_zero_or_top (hf : AnalyticOnNhd 𝕜 f U) : {u : U | analyticOrderAt f u = 0 ∨ analyticOrderAt f u = ⊤} ∈ Filter.codiscrete U := by - rw [mem_codiscrete_subtype_iff_mem_codiscreteWithin, mem_codiscreteWithin] + simp_rw [mem_codiscrete_subtype_iff_mem_codiscreteWithin, mem_codiscreteWithin, + disjoint_principal_right] intro x hx - rw [Filter.disjoint_principal_right] rcases (hf x hx).eventually_eq_zero_or_eventually_ne_zero with h₁f | h₁f · filter_upwards [eventually_nhdsWithin_of_eventually_nhds h₁f.eventually_nhds] with a ha - simp +contextual [analyticOrderAt_eq_top, ha] + simp [analyticOrderAt_eq_top, ha] · filter_upwards [h₁f] with a ha simp +contextual [(hf a _).analyticOrderAt_eq_zero, ha] +/-- +The set where an analytic function has zero or infinite order is discrete within its domain of +analyticity. +-/ +theorem codiscreteWithin_setOf_analyticOrderAt_eq_zero_or_top (hf : AnalyticOnNhd 𝕜 f U) : + {u : 𝕜 | analyticOrderAt f u = 0 ∨ analyticOrderAt f u = ⊤} ∈ codiscreteWithin U := by + simp_rw [mem_codiscreteWithin, disjoint_principal_right] + intro x hx + rcases (hf x hx).eventually_eq_zero_or_eventually_ne_zero with h₁f | h₁f + · filter_upwards [eventually_nhdsWithin_of_eventually_nhds h₁f.eventually_nhds] with a ha + simp [analyticOrderAt_eq_top, ha] + · filter_upwards [h₁f] with a ha + simp +contextual [(hf a _).analyticOrderAt_eq_zero, ha] + +/-- +If an analytic function `f` is not constantly zero on a connected set `U`, then its set of zeros is +codiscrete within `U`. + +See `AnalyticOnNhd.preimage_mem_codiscreteWithin` for a more general statement in preimages of +codiscrete sets. +-/ +theorem preimage_zero_mem_codiscreteWithin {x : 𝕜} (h₁f : AnalyticOnNhd 𝕜 f U) (h₂f : f x ≠ 0) + (hx : x ∈ U) (hU : IsConnected U) : + f ⁻¹' {0}ᶜ ∈ codiscreteWithin U := by + filter_upwards [h₁f.codiscreteWithin_setOf_analyticOrderAt_eq_zero_or_top, + self_mem_codiscreteWithin U] with a ha h₂a + rw [← (h₁f x hx).analyticOrderAt_eq_zero] at h₂f + have {u : U} : analyticOrderAt f u ≠ ⊤ := by + apply (h₁f.exists_analyticOrderAt_ne_top_iff_forall hU).1 + use ⟨x, hx⟩ + simp_all + simp_all [(h₁f a h₂a).analyticOrderAt_eq_zero] + +/-- +If an analytic function `f` is not constantly zero on `𝕜`, then its set of zeros is codiscrete. + +See `AnalyticOnNhd.preimage_mem_codiscreteWithin` for a more general statement in preimages of +codiscrete sets. +-/ +theorem preimage_zero_mem_codiscrete [ConnectedSpace 𝕜] {x : 𝕜} (hf : AnalyticOnNhd 𝕜 f Set.univ) + (hx : f x ≠ 0) : + f ⁻¹' {0}ᶜ ∈ codiscrete 𝕜 := + hf.preimage_zero_mem_codiscreteWithin hx trivial isConnected_univ + end AnalyticOnNhd diff --git a/Mathlib/Analysis/Asymptotics/LinearGrowth.lean b/Mathlib/Analysis/Asymptotics/LinearGrowth.lean index 2170f86a50ea25..17957da0729d29 100644 --- a/Mathlib/Analysis/Asymptotics/LinearGrowth.lean +++ b/Mathlib/Analysis/Asymptotics/LinearGrowth.lean @@ -9,7 +9,7 @@ import Mathlib.Analysis.SpecificLimits.Basic # Linear growth This file defines the linear growth of a sequence `u : ℕ → R`. This notion comes in two -versions, using a `liminf` and a `limsup` respectively. Most properties are developped for +versions, using a `liminf` and a `limsup` respectively. Most properties are developed for `R = EReal`. ## Main definitions diff --git a/Mathlib/Analysis/BoxIntegral/Partition/Filter.lean b/Mathlib/Analysis/BoxIntegral/Partition/Filter.lean index 7d60d3bc7d7ed2..0c40bd63da3ca7 100644 --- a/Mathlib/Analysis/BoxIntegral/Partition/Filter.lean +++ b/Mathlib/Analysis/BoxIntegral/Partition/Filter.lean @@ -202,8 +202,6 @@ namespace IntegrationParams def equivProd : IntegrationParams ≃ Bool × Boolᵒᵈ × Boolᵒᵈ where toFun l := ⟨l.1, OrderDual.toDual l.2, OrderDual.toDual l.3⟩ invFun l := ⟨l.1, OrderDual.ofDual l.2.1, OrderDual.ofDual l.2.2⟩ - left_inv _ := rfl - right_inv _ := rfl instance : PartialOrder IntegrationParams := PartialOrder.lift equivProd equivProd.injective diff --git a/Mathlib/Analysis/Convex/Basic.lean b/Mathlib/Analysis/Convex/Basic.lean index 4593480838e8aa..44e41126f5f4ca 100644 --- a/Mathlib/Analysis/Convex/Basic.lean +++ b/Mathlib/Analysis/Convex/Basic.lean @@ -678,7 +678,6 @@ def stdSimplexEquivIcc : stdSimplex 𝕜 (Fin 2) ≃ Icc (0 : 𝕜) 1 where calc (1 : 𝕜) - f.1 0 = f.1 0 + f.1 1 - f.1 0 := by rw [← Fin.sum_univ_two f.1, f.2.2] _ = f.1 1 := add_sub_cancel_left _ _ - right_inv _ := Subtype.eq rfl end OrderedRing diff --git a/Mathlib/Analysis/Convex/Cone/Basic.lean b/Mathlib/Analysis/Convex/Cone/Basic.lean new file mode 100644 index 00000000000000..5eb88446a3db3e --- /dev/null +++ b/Mathlib/Analysis/Convex/Cone/Basic.lean @@ -0,0 +1,150 @@ +/- +Copyright (c) 2022 Apurva Nakade. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Apurva Nakade, Yaël Dillies +-/ +import Mathlib.Analysis.Convex.Cone.Closure +import Mathlib.Topology.Algebra.Module.ClosedSubmodule +import Mathlib.Topology.Algebra.Order.Module +import Mathlib.Topology.Order.OrderClosed + +/-! +# Proper cones + +We define a *proper cone* as a closed, pointed cone. Proper cones are used in defining conic +programs which generalize linear programs. A linear program is a conic program for the positive +cone. We then prove Farkas' lemma for conic programs following the proof in the reference below. +Farkas' lemma is equivalent to strong duality. So, once we have the definitions of conic and +linear programs, the results from this file can be used to prove duality theorems. + +## TODO + +The next steps are: +- Add convex_cone_class that extends set_like and replace the below instance +- Define primal and dual cone programs and prove weak duality. +- Prove regular and strong duality for cone programs using Farkas' lemma (see reference). +- Define linear programs and prove LP duality as a special case of cone duality. +- Find a better reference (textbook instead of lecture notes). + +## References + +- [B. Gartner and J. Matousek, Cone Programming][gartnerMatousek] + +-/ + +open ContinuousLinearMap Filter Function Set + +variable {R E F G : Type*} [Semiring R] [PartialOrder R] [IsOrderedRing R] +variable [AddCommMonoid E] [TopologicalSpace E] [Module R E] +variable [AddCommMonoid F] [TopologicalSpace F] [Module R F] +variable [AddCommMonoid G] [TopologicalSpace G] [Module R G] + +local notation "R≥0" => {r : R // 0 ≤ r} + +variable (R E) in +/-- A proper cone is a pointed cone `C` that is closed. Proper cones have the nice property that +they are equal to their double dual, see `ProperCone.dual_dual`. +This makes them useful for defining cone programs and proving duality theorems. -/ +abbrev ProperCone := ClosedSubmodule R≥0 E + +namespace ProperCone +section Module +variable {C C₁ C₂ : ProperCone R E} {r : R} {x : E} + +/-- Any proper cone can be seen as a pointed cone. + +This is an alias of `ClosedSubmodule.toSubmodule` for convenience and discoverability. -/ +@[coe] abbrev toPointedCone (C : ProperCone R E) : PointedCone R E := C.toSubmodule + +instance : Coe (ProperCone R E) (PointedCone R E) := ⟨toPointedCone⟩ + +lemma toPointedCone_injective : Injective ((↑) : ProperCone R E → PointedCone R E) := + ClosedSubmodule.toSubmodule_injective + +-- TODO: add `ConvexConeClass` that extends `SetLike` and replace the below instance +instance : SetLike (ProperCone R E) E where + coe C := C.carrier + coe_injective' _ _ h := ProperCone.toPointedCone_injective <| SetLike.coe_injective h + +@[ext] lemma ext (h : ∀ x, x ∈ C₁ ↔ x ∈ C₂) : C₁ = C₂ := SetLike.ext h + +@[simp] lemma mem_toPointedCone : x ∈ C.toPointedCone ↔ x ∈ C := .rfl + +@[deprecated (since := "2025-06-11")] alias mem_coe := mem_toPointedCone + +lemma pointed_toConvexCone (C : ProperCone R E) : (C : ConvexCone R E).Pointed := + C.toPointedCone.pointed_toConvexCone + +@[deprecated (since := "2025-06-11")] protected alias pointed := pointed_toConvexCone + +protected lemma nonempty (C : ProperCone R E) : (C : Set E).Nonempty := C.toSubmodule.nonempty +protected lemma isClosed (C : ProperCone R E) : IsClosed (C : Set E) := C.isClosed' + +protected nonrec lemma smul_mem (C : ProperCone R E) (hx : x ∈ C) (hr : 0 ≤ r) : r • x ∈ C := + C.smul_mem ⟨r, hr⟩ hx + +section T1Space +variable [T1Space E] + +lemma mem_bot : x ∈ (⊥ : ProperCone R E) ↔ x = 0 := .rfl + +@[simp, norm_cast] lemma coe_bot : (⊥ : ProperCone R E) = ({0} : Set E) := rfl +@[simp, norm_cast] lemma toPointedCone_bot : (⊥ : ProperCone R E).toPointedCone = ⊥ := rfl + +@[deprecated (since := "2025-06-11")] alias mem_zero := mem_bot +@[deprecated (since := "2025-06-11")] alias coe_zero := coe_bot +@[deprecated (since := "2025-06-11")] alias pointed_zero := pointed_toConvexCone + +end T1Space + +/-- The closure of image of a proper cone under a `R`-linear map is a proper cone. We +use continuous maps here so that the comap of f is also a map between proper cones. -/ +abbrev comap (f : E →L[R] F) (C : ProperCone R F) : ProperCone R E := + ClosedSubmodule.comap (f.restrictScalars R≥0) C + +@[simp] lemma comap_id (C : ProperCone R F) : C.comap (.id _ _) = C := rfl + +@[simp] lemma coe_comap (f : E →L[R] F) (C : ProperCone R F) : (C.comap f : Set E) = f ⁻¹' C := rfl + +lemma comap_comap (g : F →L[R] G) (f : E →L[R] F) (C : ProperCone R G) : + (C.comap g).comap f = C.comap (g.comp f) := rfl + +lemma mem_comap {C : ProperCone R F} {f : E →L[R] F} : x ∈ C.comap f ↔ f x ∈ C := .rfl + +variable [ContinuousAdd F] [ContinuousConstSMul R F] + +/-- The closure of image of a proper cone under a linear map is a proper cone. + +We use continuous maps here to match `ProperCone.comap`. -/ +abbrev map (f : E →L[R] F) (C : ProperCone R E) : ProperCone R F := + ClosedSubmodule.map (f.restrictScalars R≥0) C + +@[simp] lemma map_id (C : ProperCone R F) : C.map (.id _ _) = C := ClosedSubmodule.map_id _ + +@[simp, norm_cast] +lemma coe_map (f : E →L[R] F) (C : ProperCone R E) : + C.map f = (C.toPointedCone.map (f : E →ₗ[R] F)).closure := rfl + +@[simp] +lemma mem_map {f : E →L[R] F} {C : ProperCone R E} {y : F} : + y ∈ C.map f ↔ y ∈ (C.toPointedCone.map (f : E →ₗ[R] F)).closure := .rfl + +end Module + +section PositiveCone +variable {E : Type*} [AddCommGroup E] [TopologicalSpace E] [Module R E] [PartialOrder E] + [IsOrderedAddMonoid E] [OrderedSMul R E] [OrderClosedTopology E] {x : E} + +variable (R E) in +/-- The positive cone is the proper cone formed by the set of nonnegative elements in an ordered +module. -/ +@[simps!] +def positive : ProperCone R E where + toSubmodule := PointedCone.positive R E + isClosed' := isClosed_Ici + +@[simp] lemma mem_positive : x ∈ positive R E ↔ 0 ≤ x := .rfl +@[simp] lemma toPointedCone_positive : (positive R E).toPointedCone = .positive R E := rfl + +end PositiveCone +end ProperCone diff --git a/Mathlib/Analysis/Convex/Cone/Closure.lean b/Mathlib/Analysis/Convex/Cone/Closure.lean index f6184697888d5d..b1ff39f678f365 100644 --- a/Mathlib/Analysis/Convex/Cone/Closure.lean +++ b/Mathlib/Analysis/Convex/Cone/Closure.lean @@ -3,7 +3,9 @@ Copyright (c) 2023 Apurva Nakade. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Apurva Nakade -/ -import Mathlib.Analysis.Convex.Cone.Pointed +import Mathlib.Geometry.Convex.Cone.Pointed +import Mathlib.Topology.Algebra.ConstMulAction +import Mathlib.Topology.Algebra.Monoid.Defs /-! # Closure of cones diff --git a/Mathlib/Analysis/Convex/Cone/InnerDual.lean b/Mathlib/Analysis/Convex/Cone/InnerDual.lean index cdcf164605848c..4fa27fed744593 100644 --- a/Mathlib/Analysis/Convex/Cone/InnerDual.lean +++ b/Mathlib/Analysis/Convex/Cone/InnerDual.lean @@ -3,8 +3,9 @@ Copyright (c) 2021 Alexander Bentkamp. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Alexander Bentkamp -/ +import Mathlib.Analysis.Convex.Cone.Basic +import Mathlib.Analysis.InnerProductSpace.Adjoint import Mathlib.Analysis.InnerProductSpace.Projection -import Mathlib.Geometry.Convex.Cone.Basic /-! # Convex cones in inner product spaces @@ -140,6 +141,37 @@ theorem ConvexCone.pointed_of_nonempty_of_isClosed (K : ConvexCone ℝ H) (ne : have f₀ : f 0 = 0 := zero_smul ℝ x simpa only [f₀, ConvexCone.Pointed, ← SetLike.mem_coe] using mem_of_subset_of_mem clf mem₀ +namespace PointedCone + +/-- The inner dual cone of a pointed cone is a pointed cone. -/ +def dual (C : PointedCone ℝ H) : PointedCone ℝ H := + ((C : Set H).innerDualCone).toPointedCone <| pointed_innerDualCone (C : Set H) + +@[simp, norm_cast] +lemma toConvexCone_dual (C : PointedCone ℝ H) : ↑(dual C) = (C : Set H).innerDualCone := rfl + +open InnerProductSpace in +@[simp] +lemma mem_dual {C : PointedCone ℝ H} {y : H} : y ∈ dual C ↔ ∀ ⦃x⦄, x ∈ C → 0 ≤ ⟪x, y⟫_ℝ := .rfl + +end PointedCone + +namespace ProperCone + +/-- The inner dual cone of a proper cone is a proper cone. -/ +def dual (C : ProperCone ℝ H) : ProperCone ℝ H where + toSubmodule := PointedCone.dual (C : PointedCone ℝ H) + isClosed' := isClosed_innerDualCone _ + +@[simp, norm_cast] +lemma coe_dual (C : ProperCone ℝ H) : C.dual = (C : Set H).innerDualCone := rfl + +open scoped InnerProductSpace in +@[simp] +lemma mem_dual {C : ProperCone ℝ H} {y : H} : y ∈ dual C ↔ ∀ ⦃x⦄, x ∈ C → 0 ≤ ⟪x, y⟫_ℝ := .rfl + +end ProperCone + section CompleteSpace variable [CompleteSpace H] @@ -196,6 +228,63 @@ theorem ConvexCone.innerDualCone_of_innerDualCone_eq_self (K : ConvexCone ℝ H) specialize h x hxK rwa [real_inner_comm] +namespace ProperCone +variable {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℝ F] + +/-- The dual of the dual of a proper cone is itself. -/ +@[simp] +theorem dual_dual (K : ProperCone ℝ H) : K.dual.dual = K := + ProperCone.toPointedCone_injective <| PointedCone.toConvexCone_injective <| + (K : ConvexCone ℝ H).innerDualCone_of_innerDualCone_eq_self K.nonempty K.isClosed + +variable [CompleteSpace F] + +open scoped InnerProductSpace + +/-- This is a relative version of +`ConvexCone.hyperplane_separation_of_nonempty_of_isClosed_of_notMem`, which we recover by setting +`f` to be the identity map. This is also a geometric interpretation of the Farkas' lemma +stated using proper cones. -/ +theorem hyperplane_separation (K : ProperCone ℝ H) {f : H →L[ℝ] F} {b : F} : + b ∈ K.map f ↔ ∀ y : F, f.adjoint y ∈ K.dual → 0 ≤ ⟪y, b⟫_ℝ where + mp := by + -- suppose `b ∈ K.map f` + simp only [mem_map, PointedCone.mem_closure, PointedCone.coe_map, ContinuousLinearMap.coe_coe, + mem_closure_iff_seq_limit, mem_image, SetLike.mem_coe, mem_dual, + ContinuousLinearMap.adjoint_inner_right, forall_exists_index, and_imp] + -- there is a sequence `seq : ℕ → F` in the image of `f` that converges to `b` + rintro seq hmem htends y hinner + suffices h : ∀ n, 0 ≤ ⟪y, seq n⟫_ℝ from + ge_of_tendsto' (Continuous.seqContinuous (Continuous.inner (@continuous_const _ _ _ _ y) + continuous_id) htends) h + intro n + obtain ⟨_, h, hseq⟩ := hmem n + simpa only [← hseq, real_inner_comm] using hinner h + mpr := by + -- proof by contradiction + -- suppose `b ∉ K.map f` + intro h + contrapose! h + -- as `b ∉ K.map f`, there is a hyperplane `y` separating `b` from `K.map f` + let C := PointedCone.toConvexCone (𝕜 := ℝ) (E := F) (K.map f) + obtain ⟨y, hxy, hyb⟩ := + @ConvexCone.hyperplane_separation_of_nonempty_of_isClosed_of_notMem + _ _ _ _ C (K.map f).nonempty (K.map f).isClosed b h + -- the rest of the proof is a straightforward algebraic manipulation + refine ⟨y, ?_, hyb⟩ + simp only [mem_dual, ContinuousLinearMap.adjoint_inner_right] + intro x hxK + exact hxy (f x) <| subset_closure <| Set.mem_image_of_mem _ hxK + +theorem hyperplane_separation_of_notMem (K : ProperCone ℝ H) {f : H →L[ℝ] F} {b : F} + (disj : b ∉ K.map f) : ∃ y : F, ContinuousLinearMap.adjoint f y ∈ K.dual ∧ ⟪y, b⟫_ℝ < 0 := by + contrapose! disj; rwa [K.hyperplane_separation] + +@[deprecated (since := "2025-05-24")] +alias hyperplane_separation_of_nmem := hyperplane_separation_of_notMem + +end ProperCone + end CompleteSpace end Dual diff --git a/Mathlib/Analysis/Convex/Cone/Proper.lean b/Mathlib/Analysis/Convex/Cone/Proper.lean deleted file mode 100644 index 137df19e526ab2..00000000000000 --- a/Mathlib/Analysis/Convex/Cone/Proper.lean +++ /dev/null @@ -1,265 +0,0 @@ -/- -Copyright (c) 2022 Apurva Nakade. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Apurva Nakade --/ -import Mathlib.Analysis.Convex.Cone.Closure -import Mathlib.Analysis.InnerProductSpace.Adjoint - -/-! -# Proper cones - -We define a *proper cone* as a closed, pointed cone. Proper cones are used in defining conic -programs which generalize linear programs. A linear program is a conic program for the positive -cone. We then prove Farkas' lemma for conic programs following the proof in the reference below. -Farkas' lemma is equivalent to strong duality. So, once we have the definitions of conic and -linear programs, the results from this file can be used to prove duality theorems. - -## TODO - -The next steps are: -- Add convex_cone_class that extends set_like and replace the below instance -- Define primal and dual cone programs and prove weak duality. -- Prove regular and strong duality for cone programs using Farkas' lemma (see reference). -- Define linear programs and prove LP duality as a special case of cone duality. -- Find a better reference (textbook instead of lecture notes). - -## References - -- [B. Gartner and J. Matousek, Cone Programming][gartnerMatousek] - --/ - -open ContinuousLinearMap Filter Set - -/-- A proper cone is a pointed cone `K` that is closed. Proper cones have the nice property that -they are equal to their double dual, see `ProperCone.dual_dual`. -This makes them useful for defining cone programs and proving duality theorems. -/ -structure ProperCone (𝕜 : Type*) (E : Type*) - [Semiring 𝕜] [PartialOrder 𝕜] [IsOrderedRing 𝕜] [AddCommMonoid E] - [TopologicalSpace E] [Module 𝕜 E] extends Submodule {c : 𝕜 // 0 ≤ c} E where - isClosed' : IsClosed (carrier : Set E) - -namespace ProperCone -section Module - -variable {𝕜 : Type*} [Semiring 𝕜] [PartialOrder 𝕜] [IsOrderedRing 𝕜] -variable {E : Type*} [AddCommMonoid E] [TopologicalSpace E] [Module 𝕜 E] - -/-- A `PointedCone` is defined as an alias of submodule. We replicate the abbreviation here and -define `toPointedCone` as an alias of `toSubmodule`. -/ -abbrev toPointedCone (C : ProperCone 𝕜 E) := C.toSubmodule - -attribute [coe] toPointedCone - -instance : Coe (ProperCone 𝕜 E) (PointedCone 𝕜 E) := - ⟨toPointedCone⟩ - -theorem toPointedCone_injective : Function.Injective ((↑) : ProperCone 𝕜 E → PointedCone 𝕜 E) := - fun S T h => by cases S; cases T; congr - --- TODO: add `ConvexConeClass` that extends `SetLike` and replace the below instance -instance : SetLike (ProperCone 𝕜 E) E where - coe K := K.carrier - coe_injective' _ _ h := ProperCone.toPointedCone_injective (SetLike.coe_injective h) - -@[ext] -theorem ext {S T : ProperCone 𝕜 E} (h : ∀ x, x ∈ S ↔ x ∈ T) : S = T := - SetLike.ext h - -@[simp] -theorem mem_coe {x : E} {K : ProperCone 𝕜 E} : x ∈ (K : PointedCone 𝕜 E) ↔ x ∈ K := - Iff.rfl - -instance instZero (K : ProperCone 𝕜 E) : Zero K := PointedCone.instZero (K.toSubmodule) - -protected theorem nonempty (K : ProperCone 𝕜 E) : (K : Set E).Nonempty := - ⟨0, by { simp_rw [SetLike.mem_coe, ← ProperCone.mem_coe, Submodule.zero_mem] }⟩ - -protected theorem isClosed (K : ProperCone 𝕜 E) : IsClosed (K : Set E) := - K.isClosed' - -end Module - -section PositiveCone - -variable (𝕜 E) -variable [Semiring 𝕜] [PartialOrder 𝕜] [IsOrderedRing 𝕜] - [AddCommGroup E] [PartialOrder E] [IsOrderedAddMonoid E] [Module 𝕜 E] [OrderedSMul 𝕜 E] - [TopologicalSpace E] [OrderClosedTopology E] - -/-- The positive cone is the proper cone formed by the set of nonnegative elements in an ordered -module. -/ -def positive : ProperCone 𝕜 E where - toSubmodule := PointedCone.positive 𝕜 E - isClosed' := isClosed_Ici - -@[simp] -theorem mem_positive {x : E} : x ∈ positive 𝕜 E ↔ 0 ≤ x := - Iff.rfl - -@[simp] -theorem coe_positive : ↑(positive 𝕜 E) = ConvexCone.positive 𝕜 E := - rfl - -end PositiveCone - -section Module - -variable {𝕜 : Type*} [Semiring 𝕜] [PartialOrder 𝕜] [IsOrderedRing 𝕜] -variable {E : Type*} [AddCommMonoid E] [TopologicalSpace E] [T1Space E] [Module 𝕜 E] - -instance : Zero (ProperCone 𝕜 E) := - ⟨{ toSubmodule := 0 - isClosed' := isClosed_singleton }⟩ - -instance : Inhabited (ProperCone 𝕜 E) := - ⟨0⟩ - -@[simp] -theorem mem_zero (x : E) : x ∈ (0 : ProperCone 𝕜 E) ↔ x = 0 := - Iff.rfl - -@[simp, norm_cast] -theorem coe_zero : ↑(0 : ProperCone 𝕜 E) = (0 : ConvexCone 𝕜 E) := - rfl - -theorem pointed_zero : ((0 : ProperCone 𝕜 E) : ConvexCone 𝕜 E).Pointed := by - simp [ConvexCone.pointed_zero] - -end Module - -section InnerProductSpace - -variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℝ E] -variable {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℝ F] -variable {G : Type*} [NormedAddCommGroup G] [InnerProductSpace ℝ G] - -protected theorem pointed (K : ProperCone ℝ E) : (K : ConvexCone ℝ E).Pointed := - (K : ConvexCone ℝ E).pointed_of_nonempty_of_isClosed K.nonempty K.isClosed - -/-- The closure of image of a proper cone under a continuous `ℝ`-linear map is a proper cone. We -use continuous maps here so that the comap of f is also a map between proper cones. -/ -noncomputable def map (f : E →L[ℝ] F) (K : ProperCone ℝ E) : ProperCone ℝ F where - toSubmodule := PointedCone.closure (PointedCone.map (f : E →ₗ[ℝ] F) ↑K) - isClosed' := isClosed_closure - -@[simp, norm_cast] -theorem coe_map (f : E →L[ℝ] F) (K : ProperCone ℝ E) : - ↑(K.map f) = (PointedCone.map (f : E →ₗ[ℝ] F) ↑K).closure := - rfl - -@[simp] -theorem mem_map {f : E →L[ℝ] F} {K : ProperCone ℝ E} {y : F} : - y ∈ K.map f ↔ y ∈ (PointedCone.map (f : E →ₗ[ℝ] F) ↑K).closure := - Iff.rfl - -@[simp] -theorem map_id (K : ProperCone ℝ E) : K.map (ContinuousLinearMap.id ℝ E) = K := - ProperCone.toPointedCone_injective <| by simpa using IsClosed.closure_eq K.isClosed - -/-- The inner dual cone of a proper cone is a proper cone. -/ -def dual (K : ProperCone ℝ E) : ProperCone ℝ E where - toSubmodule := PointedCone.dual (K : PointedCone ℝ E) - isClosed' := isClosed_innerDualCone _ - -@[simp, norm_cast] -theorem coe_dual (K : ProperCone ℝ E) : K.dual = (K : Set E).innerDualCone := - rfl - -open scoped InnerProductSpace in -@[simp] -theorem mem_dual {K : ProperCone ℝ E} {y : E} : y ∈ dual K ↔ ∀ ⦃x⦄, x ∈ K → 0 ≤ ⟪x, y⟫_ℝ := by - aesop - -/-- The preimage of a proper cone under a continuous `ℝ`-linear map is a proper cone. -/ -noncomputable def comap (f : E →L[ℝ] F) (S : ProperCone ℝ F) : ProperCone ℝ E where - toSubmodule := PointedCone.comap (f : E →ₗ[ℝ] F) S - isClosed' := by - rw [PointedCone.comap] - apply IsClosed.preimage f.2 S.isClosed - -@[simp] -theorem coe_comap (f : E →L[ℝ] F) (S : ProperCone ℝ F) : (S.comap f : Set E) = f ⁻¹' S := - rfl - -@[simp] -theorem comap_id (S : ConvexCone ℝ E) : S.comap LinearMap.id = S := - SetLike.coe_injective preimage_id - -theorem comap_comap (g : F →L[ℝ] G) (f : E →L[ℝ] F) (S : ProperCone ℝ G) : - (S.comap g).comap f = S.comap (g.comp f) := - SetLike.coe_injective <| by congr - -@[simp] -theorem mem_comap {f : E →L[ℝ] F} {S : ProperCone ℝ F} {x : E} : x ∈ S.comap f ↔ f x ∈ S := - Iff.rfl - -end InnerProductSpace - -section CompleteSpace - -open scoped InnerProductSpace - -variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℝ E] [CompleteSpace E] -variable {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℝ F] [CompleteSpace F] - -/-- The dual of the dual of a proper cone is itself. -/ -@[simp] -theorem dual_dual (K : ProperCone ℝ E) : K.dual.dual = K := - ProperCone.toPointedCone_injective <| PointedCone.toConvexCone_injective <| - (K : ConvexCone ℝ E).innerDualCone_of_innerDualCone_eq_self K.nonempty K.isClosed - -/-- This is a relative version of -`ConvexCone.hyperplane_separation_of_nonempty_of_isClosed_of_notMem`, which we recover by setting -`f` to be the identity map. This is also a geometric interpretation of the Farkas' lemma -stated using proper cones. -/ -theorem hyperplane_separation (K : ProperCone ℝ E) {f : E →L[ℝ] F} {b : F} : - b ∈ K.map f ↔ ∀ y : F, adjoint f y ∈ K.dual → 0 ≤ ⟪y, b⟫_ℝ := - Iff.intro - (by - -- suppose `b ∈ K.map f` - simp_rw [mem_map, PointedCone.mem_closure, PointedCone.coe_map, coe_coe, - mem_closure_iff_seq_limit, mem_image, SetLike.mem_coe, mem_coe, mem_dual, - adjoint_inner_right, forall_exists_index, and_imp] - -- there is a sequence `seq : ℕ → F` in the image of `f` that converges to `b` - rintro seq hmem htends y hinner - suffices h : ∀ n, 0 ≤ ⟪y, seq n⟫_ℝ from - ge_of_tendsto' - (Continuous.seqContinuous (Continuous.inner (@continuous_const _ _ _ _ y) continuous_id) - htends) - h - intro n - obtain ⟨_, h, hseq⟩ := hmem n - simpa only [← hseq, real_inner_comm] using hinner h) - (by - -- proof by contradiction - -- suppose `b ∉ K.map f` - intro h - contrapose! h - -- as `b ∉ K.map f`, there is a hyperplane `y` separating `b` from `K.map f` - let C := PointedCone.toConvexCone (𝕜 := ℝ) (E := F) (K.map f) - obtain ⟨y, hxy, hyb⟩ := - @ConvexCone.hyperplane_separation_of_nonempty_of_isClosed_of_notMem - _ _ _ _ C (K.map f).nonempty (K.map f).isClosed b h - -- the rest of the proof is a straightforward algebraic manipulation - refine ⟨y, ?_, hyb⟩ - simp_rw [ProperCone.mem_dual, adjoint_inner_right] - intro x hxK - apply hxy (f x) - simp_rw [C, coe_map] - apply subset_closure - simp_rw [PointedCone.toConvexCone_map, ConvexCone.coe_map, coe_coe, mem_image, - SetLike.mem_coe] - exact ⟨x, hxK, rfl⟩) - -theorem hyperplane_separation_of_notMem (K : ProperCone ℝ E) {f : E →L[ℝ] F} {b : F} - (disj : b ∉ K.map f) : ∃ y : F, adjoint f y ∈ K.dual ∧ ⟪y, b⟫_ℝ < 0 := by - contrapose! disj; rwa [K.hyperplane_separation] - -@[deprecated (since := "2025-05-24")] -alias hyperplane_separation_of_nmem := hyperplane_separation_of_notMem - -end CompleteSpace - -end ProperCone diff --git a/Mathlib/Analysis/Convex/Cone/README.md b/Mathlib/Analysis/Convex/Cone/README.md index cd9e169ca7ea14..b24e565df1abc6 100644 --- a/Mathlib/Analysis/Convex/Cone/README.md +++ b/Mathlib/Analysis/Convex/Cone/README.md @@ -1,12 +1,15 @@ -# Theory of convex cones in normed spaces +# Topological and analytic theory of convex cones -This subfolder is destined to contain results about convex cones that require a norm or an inner product. +This subfolder is destined to contain results about convex cones that require a topology, a norm or +an inner product. -See the `Mathlib.Geometry.Convex.Cone` folder for the results that do not need a norm nor an inner product. +See the `Mathlib.Geometry.Convex.Cone` folder for the purely algebraic theory of convex cones. ## Topics The convex cone topics currently covered are: +* Proper cone +* Dual cone along a continuous bilinear pairing * Inner dual cone * Farkas' lemma, Hahn-Banach separation, hyperplane separation, double dual of a proper cone * M. Riesz extension theorem diff --git a/Mathlib/Analysis/Convex/README.md b/Mathlib/Analysis/Convex/README.md index e24fae3edaa899..52e5561d4db665 100644 --- a/Mathlib/Analysis/Convex/README.md +++ b/Mathlib/Analysis/Convex/README.md @@ -7,7 +7,7 @@ See the `Mathlib.Geometry.Convex` folder for the results that do not need a norm ## Topics The topics currently covered are: -* Strongly convex functions, uniformly convex functiosn +* Strongly convex functions, uniformly convex functions * The product of convex functions is convex * Continuity of convex functions * Functions with positive second derivative are convex diff --git a/Mathlib/Analysis/InnerProductSpace/Adjoint.lean b/Mathlib/Analysis/InnerProductSpace/Adjoint.lean index 0258ad6cf02184..b0d38cf032a670 100644 --- a/Mathlib/Analysis/InnerProductSpace/Adjoint.lean +++ b/Mathlib/Analysis/InnerProductSpace/Adjoint.lean @@ -156,25 +156,43 @@ theorem eq_adjoint_iff (A : E →L[𝕜] F) (B : F →L[𝕜] E) : A = B† ↔ exact ext_inner_right 𝕜 fun y => by simp only [adjoint_inner_left, h x y] @[simp] -theorem adjoint_id : - ContinuousLinearMap.adjoint (ContinuousLinearMap.id 𝕜 E) = ContinuousLinearMap.id 𝕜 E := by - refine Eq.symm ?_ - rw [eq_adjoint_iff] +theorem _root_.LinearMap.IsSymmetric.clm_adjoint_eq {A : E →L[𝕜] E} (hA : A.IsSymmetric) : + A† = A := by + rwa [eq_comm, eq_adjoint_iff A A] + +theorem adjoint_id : (ContinuousLinearMap.id 𝕜 E)† = ContinuousLinearMap.id 𝕜 E := by simp theorem _root_.Submodule.adjoint_subtypeL (U : Submodule 𝕜 E) [CompleteSpace U] : U.subtypeL† = U.orthogonalProjection := by symm - rw [eq_adjoint_iff] - intro x u - rw [U.coe_inner, U.inner_orthogonalProjection_left_eq_right, - U.orthogonalProjection_mem_subspace_eq_self] - rfl + simp [eq_adjoint_iff] theorem _root_.Submodule.adjoint_orthogonalProjection (U : Submodule 𝕜 E) [CompleteSpace U] : (U.orthogonalProjection : E →L[𝕜] U)† = U.subtypeL := by rw [← U.adjoint_subtypeL, adjoint_adjoint] +theorem orthogonal_ker (T : E →L[𝕜] E) : + (LinearMap.ker T)ᗮ = (LinearMap.range (T†)).topologicalClosure := by + rw [← Submodule.orthogonal_orthogonal_eq_closure] + apply le_antisymm + all_goals refine Submodule.orthogonal_le fun x hx ↦ ?_ + · refine ext_inner_left 𝕜 fun y ↦ ?_ + simp [← T.adjoint_inner_left, hx _ (LinearMap.mem_range_self (T†) y)] + · rintro _ ⟨y, rfl⟩ + simp_all [T.adjoint_inner_left] + +theorem orthogonal_range (T : E →L[𝕜] E) : + (LinearMap.range T)ᗮ = LinearMap.ker (T†) := by + rw [← (LinearMap.ker (T†)).orthogonal_orthogonal, (T†).orthogonal_ker] + simp + +theorem ker_le_ker_iff_range_le_range [FiniteDimensional 𝕜 E] {T U : E →L[𝕜] E} + (hT : T.IsSymmetric) (hU : U.IsSymmetric) : + LinearMap.ker U ≤ LinearMap.ker T ↔ LinearMap.range T ≤ LinearMap.range U := by + refine ⟨fun h ↦ ?_, LinearMap.ker_le_ker_of_range hT hU⟩ + simpa [orthogonal_ker, hT, hU] using Submodule.orthogonal_le h + /-- `E →L[𝕜] E` is a star algebra with the adjoint as the star operation. -/ instance : Star (E →L[𝕜] E) := ⟨adjoint⟩ @@ -195,11 +213,11 @@ theorem star_eq_adjoint (A : E →L[𝕜] E) : star A = A† := rfl /-- A continuous linear operator is self-adjoint iff it is equal to its adjoint. -/ -theorem isSelfAdjoint_iff' {A : E →L[𝕜] E} : IsSelfAdjoint A ↔ ContinuousLinearMap.adjoint A = A := +theorem isSelfAdjoint_iff' {A : E →L[𝕜] E} : IsSelfAdjoint A ↔ A† = A := Iff.rfl theorem norm_adjoint_comp_self (A : E →L[𝕜] F) : - ‖ContinuousLinearMap.adjoint A ∘L A‖ = ‖A‖ * ‖A‖ := by + ‖A† ∘L A‖ = ‖A‖ * ‖A‖ := by refine le_antisymm ?_ ?_ · calc ‖A† ∘L A‖ ≤ ‖A†‖ * ‖A‖ := opNorm_comp_le _ _ @@ -314,12 +332,12 @@ vector spaces. Use local instances instead. -/ /-- The adjoint of an operator from the finite-dimensional inner product space `E` to the finite-dimensional inner product space `F`. -/ def adjoint : (E →ₗ[𝕜] F) ≃ₗ⋆[𝕜] F →ₗ[𝕜] E := - have := FiniteDimensional.complete 𝕜 E - have := FiniteDimensional.complete 𝕜 F + haveI := FiniteDimensional.complete 𝕜 E + haveI := FiniteDimensional.complete 𝕜 F /- Note: Instead of the two instances above, the following works: ``` - have := FiniteDimensional.complete 𝕜 - have := FiniteDimensional.complete 𝕜 + haveI := FiniteDimensional.complete 𝕜 + haveI := FiniteDimensional.complete 𝕜 ``` But removing one of the `have`s makes it fail. The reason is that `E` and `F` don't live in the same universe, so the first `have` can no longer be used for `F` after its universe @@ -344,15 +362,15 @@ theorem adjoint_eq_toCLM_adjoint (A : E →ₗ[𝕜] F) : /-- The fundamental property of the adjoint. -/ theorem adjoint_inner_left (A : E →ₗ[𝕜] F) (x : E) (y : F) : ⟪adjoint A y, x⟫ = ⟪y, A x⟫ := by - haveI := FiniteDimensional.complete 𝕜 E - haveI := FiniteDimensional.complete 𝕜 F + have := FiniteDimensional.complete 𝕜 E + have := FiniteDimensional.complete 𝕜 F rw [← coe_toContinuousLinearMap A, adjoint_eq_toCLM_adjoint] exact ContinuousLinearMap.adjoint_inner_left _ x y /-- The fundamental property of the adjoint. -/ theorem adjoint_inner_right (A : E →ₗ[𝕜] F) (x : E) (y : F) : ⟪x, adjoint A y⟫ = ⟪A x, y⟫ := by - haveI := FiniteDimensional.complete 𝕜 E - haveI := FiniteDimensional.complete 𝕜 F + have := FiniteDimensional.complete 𝕜 E + have := FiniteDimensional.complete 𝕜 F rw [← coe_toContinuousLinearMap A, adjoint_eq_toCLM_adjoint] exact ContinuousLinearMap.adjoint_inner_right _ x y @@ -380,6 +398,14 @@ theorem eq_adjoint_iff (A : E →ₗ[𝕜] F) (B : F →ₗ[𝕜] E) : ext x exact ext_inner_right 𝕜 fun y => by simp only [adjoint_inner_left, h x y] +@[simp] +theorem IsSymmetric.adjoint_eq {A : E →ₗ[𝕜] E} (hA : A.IsSymmetric) : + A.adjoint = A := by + rwa [eq_comm, eq_adjoint_iff A A] + +theorem adjoint_id : (LinearMap.id (R := 𝕜) (M := E)).adjoint = LinearMap.id := by + simp + /-- The adjoint is unique: a map `A` is the adjoint of `B` iff it satisfies `⟪A x, y⟫ = ⟪x, B y⟫` for all basis vectors `x` and `y`. -/ theorem eq_adjoint_iff_basis {ι₁ : Type*} {ι₂ : Type*} (b₁ : Basis ι₁ 𝕜 E) (b₂ : Basis ι₂ 𝕜 F) @@ -546,8 +572,6 @@ noncomputable def linearIsometryEquiv : unitary (H →L[𝕜] H) ≃* (H ≃ₗ inv_val := by ext; simp } exact IsUnit.mem_unitary_of_star_mul_self ⟨e', rfl⟩ <| (e : H →L[𝕜] H).norm_map_iff_adjoint_comp_self.mp e.norm_map } - left_inv _ := Subtype.ext rfl - right_inv _ := LinearIsometryEquiv.ext fun _ ↦ rfl map_mul' u v := by ext; rfl @[simp] diff --git a/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean b/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean index 021ac00fe674b1..87944301d3d491 100644 --- a/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean +++ b/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean @@ -182,6 +182,12 @@ theorem orthogonal_eq_top_iff : Kᗮ = ⊤ ↔ K = ⊥ := by have : K ⊓ Kᗮ = ⊥ := K.orthogonal_disjoint.eq_bot rwa [h, inf_comm, top_inf_eq] at this +/-- The closure of a submodule has the same orthogonal complement and the submodule itself. -/ +@[simp] +lemma orthogonal_closure (K : Submodule 𝕜 E) : K.topologicalClosureᗮ = Kᗮ := + le_antisymm (orthogonal_le <| le_topologicalClosure _) + fun x hx y hy ↦ closure_minimal hx (isClosed_eq (by fun_prop) (by fun_prop)) hy + theorem orthogonalFamily_self : OrthogonalFamily 𝕜 (fun b => ↥(cond b K Kᗮ)) fun b => (cond b K Kᗮ).subtypeₗᵢ | true, true => absurd rfl diff --git a/Mathlib/Analysis/InnerProductSpace/PiL2.lean b/Mathlib/Analysis/InnerProductSpace/PiL2.lean index 24126002dc6dc4..9cb4e816181216 100644 --- a/Mathlib/Analysis/InnerProductSpace/PiL2.lean +++ b/Mathlib/Analysis/InnerProductSpace/PiL2.lean @@ -350,7 +350,7 @@ instance instFunLike : FunLike (OrthonormalBasis ι 𝕜 E) ι E where replace h := congr_fun h i simp only [LinearEquiv.comp_coe, map_smul, LinearEquiv.coe_coe, LinearEquiv.trans_apply, WithLp.linearEquiv_symm_apply, WithLp.equiv_symm_single, - LinearIsometryEquiv.coe_toLinearEquiv] at h ⊢ + LinearIsometryEquiv.coe_symm_toLinearEquiv] at h ⊢ rw [h] @[simp] diff --git a/Mathlib/Analysis/InnerProductSpace/Positive.lean b/Mathlib/Analysis/InnerProductSpace/Positive.lean index 7c88819fd32b7d..2a22ab599267eb 100644 --- a/Mathlib/Analysis/InnerProductSpace/Positive.lean +++ b/Mathlib/Analysis/InnerProductSpace/Positive.lean @@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Anatole Dedecker -/ import Mathlib.Analysis.InnerProductSpace.Adjoint +import Mathlib.Analysis.InnerProductSpace.Spectrum /-! # Positive operators @@ -331,6 +332,13 @@ theorem IsPositive.adjoint_conj {T : E →ₗ[𝕜] E} (hT : T.IsPositive) (S : convert hT.conj_adjoint S.adjoint rw [adjoint_adjoint] +theorem IsPositive.nonneg_eigenvalues {T : E →ₗ[𝕜] E} {n : ℕ} (hT : T.IsPositive) + (hn : Module.finrank 𝕜 E = n) (i : Fin n) : 0 ≤ hT.isSymmetric.eigenvalues hn i := by + have h := hT.right (hT.isSymmetric.eigenvectorBasis hn i) + rw [hT.isSymmetric.apply_eigenvectorBasis, inner_smul_real_left, RCLike.smul_re, + inner_self_eq_norm_sq, OrthonormalBasis.norm_eq_one, one_pow, mul_one] at h + exact h + section PartialOrder /-- The (Loewner) partial order on linear maps on a Hilbert space determined by `f ≤ g` diff --git a/Mathlib/Analysis/InnerProductSpace/Projection.lean b/Mathlib/Analysis/InnerProductSpace/Projection.lean index 58615f42d0957f..7e61b0dba37ffa 100644 --- a/Mathlib/Analysis/InnerProductSpace/Projection.lean +++ b/Mathlib/Analysis/InnerProductSpace/Projection.lean @@ -772,6 +772,18 @@ theorem orthogonal_orthogonal [K.HasOrthogonalProjection] : Kᗮᗮ = K := by rw [inner_eq_zero_symm] exact hw v hv +lemma orthogonal_le_orthogonal_iff {K₀ K₁ : Submodule 𝕜 E} [K₀.HasOrthogonalProjection] + [K₁.HasOrthogonalProjection] : K₀ᗮ ≤ K₁ᗮ ↔ K₁ ≤ K₀ := + ⟨fun h ↦ by simpa using orthogonal_le h, orthogonal_le⟩ + +lemma orthogonal_le_iff_orthogonal_le {K₀ K₁ : Submodule 𝕜 E} [K₀.HasOrthogonalProjection] + [K₁.HasOrthogonalProjection] : K₀ᗮ ≤ K₁ ↔ K₁ᗮ ≤ K₀ := by + rw [← orthogonal_le_orthogonal_iff, orthogonal_orthogonal] + +lemma le_orthogonal_iff_le_orthogonal {K₀ K₁ : Submodule 𝕜 E} [K₀.HasOrthogonalProjection] + [K₁.HasOrthogonalProjection] : K₀ ≤ K₁ᗮ ↔ K₁ ≤ K₀ᗮ := by + rw [← orthogonal_le_orthogonal_iff, orthogonal_orthogonal] + /-- In a Hilbert space, the orthogonal complement of the orthogonal complement of a subspace `K` is the topological closure of `K`. @@ -986,6 +998,10 @@ open Module variable [FiniteDimensional 𝕜 K] +@[simp] +theorem topologicalClosure_eq_self : K.topologicalClosure = K := + K.closed_of_finiteDimensional.submodule_topologicalClosure_eq + @[simp] theorem det_reflection : LinearMap.det K.reflection.toLinearMap = (-1) ^ finrank 𝕜 Kᗮ := by by_cases hK : FiniteDimensional 𝕜 Kᗮ diff --git a/Mathlib/Analysis/InnerProductSpace/Symmetric.lean b/Mathlib/Analysis/InnerProductSpace/Symmetric.lean index 8f064f3c39cff9..3c3d6550482aee 100644 --- a/Mathlib/Analysis/InnerProductSpace/Symmetric.lean +++ b/Mathlib/Analysis/InnerProductSpace/Symmetric.lean @@ -234,6 +234,13 @@ theorem IsSymmetric.inner_map_self_eq_zero {T : E →ₗ[𝕜] E} (hT : T.IsSymm simp_rw [h _] ring +theorem ker_le_ker_of_range {S T : E →ₗ[𝕜] E} (hS : S.IsSymmetric) (hT : T.IsSymmetric) + (h : range S ≤ range T) : ker T ≤ ker S := by + intro v hv + rw [mem_ker] at hv ⊢ + obtain ⟨y, hy⟩ : ∃ y, T y = S (S v) := by simpa using @h (S (S v)) + rw [← inner_self_eq_zero (𝕜 := 𝕜), ← hS, ← hy, hT, hv, inner_zero_right] + end LinearMap end Normed diff --git a/Mathlib/Analysis/InnerProductSpace/TwoDim.lean b/Mathlib/Analysis/InnerProductSpace/TwoDim.lean index e93594459a7c90..feaa8986e4d768 100644 --- a/Mathlib/Analysis/InnerProductSpace/TwoDim.lean +++ b/Mathlib/Analysis/InnerProductSpace/TwoDim.lean @@ -173,9 +173,9 @@ irreducible_def rightAngleRotationAux₁ : E →ₗ[ℝ] E := @[simp] theorem inner_rightAngleRotationAux₁_left (x y : E) : ⟪o.rightAngleRotationAux₁ x, y⟫ = ω x y := by - simp only [rightAngleRotationAux₁, LinearEquiv.trans_symm, LinearIsometryEquiv.toLinearEquiv_symm, + simp only [rightAngleRotationAux₁, LinearEquiv.trans_symm, LinearEquiv.symm_symm, LinearMap.coe_comp, LinearEquiv.coe_coe, Function.comp_apply, LinearEquiv.trans_apply, - LinearIsometryEquiv.coe_toLinearEquiv] + LinearIsometryEquiv.coe_symm_toLinearEquiv] rw [InnerProductSpace.toDual_symm_apply] norm_cast diff --git a/Mathlib/Analysis/Meromorphic/IsolatedZeros.lean b/Mathlib/Analysis/Meromorphic/IsolatedZeros.lean index 2b3fec8eb1d06e..f79872881c1533 100644 --- a/Mathlib/Analysis/Meromorphic/IsolatedZeros.lean +++ b/Mathlib/Analysis/Meromorphic/IsolatedZeros.lean @@ -6,13 +6,13 @@ Authors: Stefan Kebekus import Mathlib.Analysis.Meromorphic.Basic /-! -# Principles of Isolated Zeros and Identitiy Principles for Meromorphic Functions +# Principles of Isolated Zeros and Identity Principles for Meromorphic Functions In line with results in `Mathlib.Analysis.Analytic.IsolatedZeros` and `Mathlib.Analysis.Analytic.Uniqueness`, this file establishes principles of isolated zeros and identity principles for meromorphic functions. -Compared to the results for analytic functions, the principles established here are a litte more +Compared to the results for analytic functions, the principles established here are a little more complicated to state. This is because meromorphic functions can be modified at will along discrete subsets and still remain meromorphic. -/ diff --git a/Mathlib/Analysis/Normed/Affine/Isometry.lean b/Mathlib/Analysis/Normed/Affine/Isometry.lean index 3fd03f69c9b604..10fe8c72d97e5c 100644 --- a/Mathlib/Analysis/Normed/Affine/Isometry.lean +++ b/Mathlib/Analysis/Normed/Affine/Isometry.lean @@ -454,15 +454,27 @@ theorem symm_bijective : Bijective (AffineIsometryEquiv.symm : (P₂ ≃ᵃⁱ[ Function.bijective_iff_has_inverse.mpr ⟨_, symm_symm, symm_symm⟩ @[simp] -theorem toAffineEquiv_symm : e.toAffineEquiv.symm = e.symm.toAffineEquiv := +theorem toAffineEquiv_symm : e.symm.toAffineEquiv = e.toAffineEquiv.symm := rfl @[simp] -theorem toIsometryEquiv_symm : e.toIsometryEquiv.symm = e.symm.toIsometryEquiv := +theorem coe_symm_toAffineEquiv : ⇑e.toAffineEquiv.symm = e.symm := rfl @[simp] -theorem toHomeomorph_symm : e.toHomeomorph.symm = e.symm.toHomeomorph := +theorem toIsometryEquiv_symm : e.symm.toIsometryEquiv = e.toIsometryEquiv.symm := + rfl + +@[simp] +theorem coe_symm_toIsometryEquiv : ⇑e.toIsometryEquiv.symm = e.symm := + rfl + +@[simp] +theorem toHomeomorph_symm : e.symm.toHomeomorph = e.toHomeomorph.symm := + rfl + +@[simp] +theorem coe_symm_toHomeomorph : ⇑e.toHomeomorph.symm = e.symm := rfl /-- Composition of `AffineIsometryEquiv`s as an `AffineIsometryEquiv`. -/ diff --git a/Mathlib/Analysis/Normed/Affine/MazurUlam.lean b/Mathlib/Analysis/Normed/Affine/MazurUlam.lean index 3f25c5ae3d8b8e..5f5d1917d3529e 100644 --- a/Mathlib/Analysis/Normed/Affine/MazurUlam.lean +++ b/Mathlib/Analysis/Normed/Affine/MazurUlam.lean @@ -89,7 +89,7 @@ theorem map_midpoint (f : PE ≃ᵢ PF) (x y : PE) : f (midpoint ℝ x y) = midp have hy : e y = y := by simp [e] have hm := e.midpoint_fixed hx hy simp only [e, trans_apply] at hm - rwa [← eq_symm_apply, toIsometryEquiv_symm, pointReflection_symm, coe_toIsometryEquiv, + rwa [← eq_symm_apply, ← toIsometryEquiv_symm, pointReflection_symm, coe_toIsometryEquiv, coe_toIsometryEquiv, pointReflection_self, symm_apply_eq, @pointReflection_fixed_iff] at hm /-! diff --git a/Mathlib/Analysis/Normed/Algebra/Spectrum.lean b/Mathlib/Analysis/Normed/Algebra/Spectrum.lean index b13879d6dd4421..a8b6b39e485461 100644 --- a/Mathlib/Analysis/Normed/Algebra/Spectrum.lean +++ b/Mathlib/Analysis/Normed/Algebra/Spectrum.lean @@ -564,8 +564,6 @@ def equivAlgHom : characterSpace 𝕜 A ≃ (A →ₐ[𝕜] 𝕜) where invFun f := { val := f.toContinuousLinearMap property := by rw [eq_set_map_one_map_mul]; exact ⟨map_one f, map_mul f⟩ } - left_inv _ := Subtype.ext <| ContinuousLinearMap.ext fun _ => rfl - right_inv _ := AlgHom.ext fun _ => rfl @[simp] theorem equivAlgHom_coe (f : characterSpace 𝕜 A) : ⇑(equivAlgHom f) = f := diff --git a/Mathlib/Analysis/Normed/Group/Hom.lean b/Mathlib/Analysis/Normed/Group/Hom.lean index 12f7c81f130267..e18e76923dd9d6 100644 --- a/Mathlib/Analysis/Normed/Group/Hom.lean +++ b/Mathlib/Analysis/Normed/Group/Hom.lean @@ -802,9 +802,6 @@ def liftEquiv : toFun φ := lift φ φ.prop invFun ψ := ⟨(ι f g).comp ψ, by rw [← comp_assoc, ← comp_assoc, comp_ι_eq]⟩ left_inv φ := by simp - right_inv ψ := by - ext - rfl /-- Given `φ : NormedAddGroupHom V₁ V₂` and `ψ : NormedAddGroupHom W₁ W₂` such that `ψ.comp f₁ = f₂.comp φ` and `ψ.comp g₁ = g₂.comp φ`, the induced morphism diff --git a/Mathlib/Analysis/Normed/Group/Seminorm.lean b/Mathlib/Analysis/Normed/Group/Seminorm.lean index 8b7a8d17bfeacb..03cfe75133c4fe 100644 --- a/Mathlib/Analysis/Normed/Group/Seminorm.lean +++ b/Mathlib/Analysis/Normed/Group/Seminorm.lean @@ -5,7 +5,6 @@ Authors: María Inés de Frutos-Fernández, Yaël Dillies -/ import Mathlib.Data.NNReal.Defs import Mathlib.Order.ConditionallyCompleteLattice.Group -import Mathlib.Tactic.GCongr.CoreAttrs /-! # Group seminorms diff --git a/Mathlib/Analysis/Normed/Group/SeparationQuotient.lean b/Mathlib/Analysis/Normed/Group/SeparationQuotient.lean index 75d5508fbadcfd..72d45634366a45 100644 --- a/Mathlib/Analysis/Normed/Group/SeparationQuotient.lean +++ b/Mathlib/Analysis/Normed/Group/SeparationQuotient.lean @@ -88,7 +88,6 @@ noncomputable def liftNormedAddGroupHomEquiv {N : Type*} [SeminormedAddCommGroup intro x hx rw [← norm_mk, norm_eq_zero] at hx simp [hx]⟩ - left_inv _ := rfl right_inv _ := by ext x obtain ⟨x, rfl⟩ := surjective_mk x diff --git a/Mathlib/Analysis/Normed/Lp/LpEquiv.lean b/Mathlib/Analysis/Normed/Lp/LpEquiv.lean index 2004458248612c..364ef5e31550b9 100644 --- a/Mathlib/Analysis/Normed/Lp/LpEquiv.lean +++ b/Mathlib/Analysis/Normed/Lp/LpEquiv.lean @@ -58,8 +58,6 @@ theorem Memℓp.all (f : ∀ i, E i) : Memℓp f p := by def Equiv.lpPiLp : lp E p ≃ PiLp p E where toFun f := ⇑f invFun f := ⟨f, Memℓp.all f⟩ - left_inv _f := rfl - right_inv _f := rfl theorem coe_equiv_lpPiLp (f : lp E p) : Equiv.lpPiLp f = ⇑f := rfl @@ -129,8 +127,6 @@ section NormedAddCommGroup noncomputable def AddEquiv.lpBCF : lp (fun _ : α ↦ E) ∞ ≃+ (α →ᵇ E) where toFun f := ofNormedAddCommGroupDiscrete f ‖f‖ <| le_ciSup (memℓp_infty_iff.mp f.prop) invFun f := ⟨⇑f, f.bddAbove_range_norm_comp⟩ - left_inv _f := lp.ext rfl - right_inv _f := rfl map_add' _f _g := rfl diff --git a/Mathlib/Analysis/Normed/Operator/Banach.lean b/Mathlib/Analysis/Normed/Operator/Banach.lean index 3d2afa9cdb8ee1..97deef965bdd78 100644 --- a/Mathlib/Analysis/Normed/Operator/Banach.lean +++ b/Mathlib/Analysis/Normed/Operator/Banach.lean @@ -365,7 +365,7 @@ theorem coe_equivRange (hinj : Injective f) (hclo : IsClosed (range f)) : @[simp] lemma equivRange_symm_toLinearEquiv (hinj : Injective f) (hclo : IsClosed (range f)) : - (f.equivRange hinj hclo).symm.toLinearEquiv = + (f.equivRange hinj hclo).toLinearEquiv.symm = (LinearEquiv.ofInjective f.toLinearMap hinj).symm := by rfl @@ -375,7 +375,7 @@ lemma equivRange_symm_apply (hinj : Injective f) (hclo : IsClosed (range f)) suffices f ((f.equivRange hinj hclo).symm ⟨f x, by simp⟩) = f x from hinj this trans f ((f.equivRange hinj hclo).symm.toLinearEquiv ⟨f x, by simp⟩) · rfl -- is there an API lemma for this already? - dsimp only [equivRange_symm_toLinearEquiv] + simp only [ContinuousLinearEquiv.toLinearEquiv_symm, equivRange_symm_toLinearEquiv] set x' : LinearMap.range f := ⟨f x, by simp⟩ set f' : E →ₛₗ[σ] F := ↑f change f' ((LinearEquiv.ofInjective f' hinj).symm x') = _ diff --git a/Mathlib/Analysis/Normed/Operator/BoundedLinearMaps.lean b/Mathlib/Analysis/Normed/Operator/BoundedLinearMaps.lean index 0b60a2c186c0a2..f8972fbdb55f28 100644 --- a/Mathlib/Analysis/Normed/Operator/BoundedLinearMaps.lean +++ b/Mathlib/Analysis/Normed/Operator/BoundedLinearMaps.lean @@ -446,22 +446,24 @@ theorem IsBoundedBilinearMap.isBoundedLinearMap_deriv (h : IsBoundedBilinearMap end BilinearMap +variable {X : Type*} [TopologicalSpace X] + @[continuity, fun_prop] -theorem Continuous.clm_comp {X} [TopologicalSpace X] {g : X → F →L[𝕜] G} {f : X → E →L[𝕜] F} +theorem Continuous.clm_comp {g : X → F →L[𝕜] G} {f : X → E →L[𝕜] F} (hg : Continuous g) (hf : Continuous f) : Continuous fun x => (g x).comp (f x) := (compL 𝕜 E F G).continuous₂.comp₂ hg hf -theorem ContinuousOn.clm_comp {X} [TopologicalSpace X] {g : X → F →L[𝕜] G} {f : X → E →L[𝕜] F} +theorem ContinuousOn.clm_comp {g : X → F →L[𝕜] G} {f : X → E →L[𝕜] F} {s : Set X} (hg : ContinuousOn g s) (hf : ContinuousOn f s) : ContinuousOn (fun x => (g x).comp (f x)) s := (compL 𝕜 E F G).continuous₂.comp_continuousOn (hg.prodMk hf) @[continuity, fun_prop] -theorem Continuous.clm_apply {X} [TopologicalSpace X] {f : X → (E →L[𝕜] F)} {g : X → E} +theorem Continuous.clm_apply {f : X → (E →L[𝕜] F)} {g : X → E} (hf : Continuous f) (hg : Continuous g) : Continuous (fun x ↦ (f x) (g x)) := isBoundedBilinearMap_apply.continuous.comp₂ hf hg -theorem ContinuousOn.clm_apply {X} [TopologicalSpace X] {f : X → (E →L[𝕜] F)} {g : X → E} +theorem ContinuousOn.clm_apply {f : X → (E →L[𝕜] F)} {g : X → E} {s : Set X} (hf : ContinuousOn f s) (hg : ContinuousOn g s) : ContinuousOn (fun x ↦ f x (g x)) s := isBoundedBilinearMap_apply.continuous.comp_continuousOn (hf.prodMk hg) diff --git a/Mathlib/Analysis/Normed/Operator/LinearIsometry.lean b/Mathlib/Analysis/Normed/Operator/LinearIsometry.lean index b34cd00c041c4c..037a4df54af6b9 100644 --- a/Mathlib/Analysis/Normed/Operator/LinearIsometry.lean +++ b/Mathlib/Analysis/Normed/Operator/LinearIsometry.lean @@ -649,17 +649,34 @@ theorem symm_bijective : Function.Bijective (symm : (E₂ ≃ₛₗᵢ[σ₂₁] Function.bijective_iff_has_inverse.mpr ⟨_, symm_symm, symm_symm⟩ @[simp] -theorem toLinearEquiv_symm : e.toLinearEquiv.symm = e.symm.toLinearEquiv := +theorem toLinearEquiv_symm : e.symm.toLinearEquiv = e.toLinearEquiv.symm := rfl @[simp] -theorem toIsometryEquiv_symm : e.toIsometryEquiv.symm = e.symm.toIsometryEquiv := +theorem coe_symm_toLinearEquiv : ⇑e.toLinearEquiv.symm = e.symm := rfl + +@[simp] +theorem toContinuousLinearEquiv_symm : + e.symm.toContinuousLinearEquiv = e.toContinuousLinearEquiv.symm := rfl + +@[simp] +theorem coe_symm_toContinuousLinearEquiv : ⇑e.toContinuousLinearEquiv.symm = e.symm := rfl @[simp] -theorem toHomeomorph_symm : e.toHomeomorph.symm = e.symm.toHomeomorph := +theorem toIsometryEquiv_symm : e.symm.toIsometryEquiv = e.toIsometryEquiv.symm := + rfl + +@[simp] +theorem coe_symm_toIsometryEquiv : ⇑e.toIsometryEquiv.symm = e.symm := rfl + +@[simp] +theorem toHomeomorph_symm : e.symm.toHomeomorph = e.toHomeomorph.symm := rfl +@[simp] +theorem coe_symm_toHomeomorph : ⇑e.toHomeomorph.symm = e.symm := rfl + /-- See Note [custom simps projection]. We need to specify this projection explicitly in this case, because it is a composition of multiple projections. -/ def Simps.apply (σ₁₂ : R →+* R₂) {σ₂₁ : R₂ →+* R} [RingHomInvPair σ₁₂ σ₂₁] [RingHomInvPair σ₂₁ σ₁₂] @@ -693,6 +710,16 @@ theorem toLinearEquiv_trans (e' : E₂ ≃ₛₗᵢ[σ₂₃] E₃) : (e.trans e').toLinearEquiv = e.toLinearEquiv.trans e'.toLinearEquiv := rfl +@[simp] +theorem toIsometryEquiv_trans (e' : E₂ ≃ₛₗᵢ[σ₂₃] E₃) : + (e.trans e').toIsometryEquiv = e.toIsometryEquiv.trans e'.toIsometryEquiv := + rfl + +@[simp] +theorem toHomeomorph_trans (e' : E₂ ≃ₛₗᵢ[σ₂₃] E₃) : + (e.trans e').toHomeomorph = e.toHomeomorph.trans e'.toHomeomorph := + rfl + @[simp] theorem trans_refl : e.trans (refl R₂ E₂) = e := ext fun _ => rfl diff --git a/Mathlib/Analysis/NormedSpace/Alternating/Basic.lean b/Mathlib/Analysis/NormedSpace/Alternating/Basic.lean index a8171a9f172e2f..ec4ea6be41a6bf 100644 --- a/Mathlib/Analysis/NormedSpace/Alternating/Basic.lean +++ b/Mathlib/Analysis/NormedSpace/Alternating/Basic.lean @@ -97,7 +97,7 @@ satisfies the inequality `‖f m‖ ≤ C * ∏ i, ‖m i‖` on a shell `ε / ‖c‖ < ‖m i‖ < ε` for some positive number `ε` and an elements `c : 𝕜`, `1 < ‖c‖`, then it satisfies this inequality for all `m`. -If the domain is a Hausdorff space, then the continuity assumption is reduntant, +If the domain is a Hausdorff space, then the continuity assumption is redundant, see `bound_of_shell` below. -/ theorem bound_of_shell_of_continuous (f : E [⋀^ι]→ₗ[𝕜] F) (hfc : Continuous f) {ε : ℝ} {C : ℝ} (hε : 0 < ε) {c : 𝕜} (hc : 1 < ‖c‖) @@ -320,8 +320,6 @@ def prodLIE : (E [⋀^ι]→L[𝕜] F) × (E [⋀^ι]→L[𝕜] G) ≃ₗᵢ[ (ContinuousLinearMap.snd 𝕜 F G).compContinuousAlternatingMap f) map_add' _ _ := rfl map_smul' _ _ := rfl - left_inv _ := rfl - right_inv _ := rfl norm_map' f := opNorm_prod f.1 f.2 variable (𝕜 E) in diff --git a/Mathlib/Analysis/NormedSpace/HahnBanach/Extension.lean b/Mathlib/Analysis/NormedSpace/HahnBanach/Extension.lean index 24a2b98382f519..fb7b638918a4d6 100644 --- a/Mathlib/Analysis/NormedSpace/HahnBanach/Extension.lean +++ b/Mathlib/Analysis/NormedSpace/HahnBanach/Extension.lean @@ -160,7 +160,7 @@ theorem exists_dual_vector (x : E) (h : x ≠ 0) : ∃ g : E →L[𝕜] 𝕜, refine ⟨g, ?_, ?_⟩ · rw [hg.2, coord_norm'] · calc - g x = g (⟨x, mem_span_singleton_self x⟩ : 𝕜 ∙ x) := by rw [coe_mk] + g x = g (⟨x, mem_span_singleton_self x⟩ : 𝕜 ∙ x) := by rw [Submodule.coe_mk] _ = ((‖x‖ : 𝕜) • coord 𝕜 x h) (⟨x, mem_span_singleton_self x⟩ : 𝕜 ∙ x) := by rw [← hg.1] _ = ‖x‖ := by simp [-algebraMap_smul] diff --git a/Mathlib/Analysis/NormedSpace/Multilinear/Curry.lean b/Mathlib/Analysis/NormedSpace/Multilinear/Curry.lean index ffa2874e81ead4..1572d1d77319d5 100644 --- a/Mathlib/Analysis/NormedSpace/Multilinear/Curry.lean +++ b/Mathlib/Analysis/NormedSpace/Multilinear/Curry.lean @@ -582,10 +582,7 @@ def currySumEquiv : ContinuousMultilinearMap 𝕜 (fun _ : ι ⊕ ι' => G) G' rfl left_inv := fun f => by ext m - exact congr_arg f (Sum.elim_comp_inl_inr m) - right_inv := fun f => by - ext m₁ m₂ - rfl } + exact congr_arg f (Sum.elim_comp_inl_inr m) } (fun f => MultilinearMap.mkContinuousMultilinear_norm_le _ (norm_nonneg f) _) fun f => by simp only [LinearEquiv.coe_symm_mk] exact MultilinearMap.mkContinuous_norm_le _ (norm_nonneg f) _ diff --git a/Mathlib/Analysis/NormedSpace/MultipliableUniformlyOn.lean b/Mathlib/Analysis/NormedSpace/MultipliableUniformlyOn.lean index c97fbe274f43e2..2415e6caaefe08 100644 --- a/Mathlib/Analysis/NormedSpace/MultipliableUniformlyOn.lean +++ b/Mathlib/Analysis/NormedSpace/MultipliableUniformlyOn.lean @@ -44,7 +44,7 @@ lemma Summable.tendstoUniformlyOn_tsum_nat_log_one_add {f : ℕ → α → ℂ} TendstoUniformlyOn (fun n x ↦ ∑ m ∈ Finset.range n, log (1 + f m x)) (fun x ↦ ∑' n, log (1 + f n x)) atTop K := by rw [← Nat.cofinite_eq_atTop] at h - exact (hu.hasSumUniformlyOn_log_one_add h).tendstoUniformlyOn_finset_range rfl + exact (hu.hasSumUniformlyOn_log_one_add h).tendstoUniformlyOn_finsetRange rfl /-- If `x ↦ ∑' i, log (f i x)` is uniformly convergent on `𝔖`, its sum has bounded-above real part on each set in `𝔖`, and the functions `f i x` have no zeroes, then `∏' i, f i x` is uniformly diff --git a/Mathlib/Analysis/RCLike/TangentCone.lean b/Mathlib/Analysis/RCLike/TangentCone.lean new file mode 100644 index 00000000000000..ebf09dad8a8455 --- /dev/null +++ b/Mathlib/Analysis/RCLike/TangentCone.lean @@ -0,0 +1,53 @@ +/- +Copyright (c) 2025 Sébastien Gouëzel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sébastien Gouëzel +-/ +import Mathlib.Analysis.Calculus.TangentCone +import Mathlib.Analysis.RCLike.Basic +import Mathlib.Topology.Instances.RealVectorSpace + +/-! # Relationships between unique differentiability over `ℝ` and `ℂ` + +A set of unique differentiability for `ℝ` is also a set of unique differentiability for `ℂ` +(or for a general field satisfying `IsRCLikeNormedField 𝕜`). +-/ + +variable {𝕜 : Type*} [NontriviallyNormedField 𝕜] [h𝕜 : IsRCLikeNormedField 𝕜] +{E : Type*} [NormedAddCommGroup E] [NormedSpace 𝕜 E] [NormedSpace ℝ E] +{s : Set E} {x : E} + +theorem tangentConeAt_real_subset_isRCLikeNormedField : + tangentConeAt ℝ s x ⊆ tangentConeAt 𝕜 s x := by + letI := h𝕜.rclike + rintro y ⟨c, d, d_mem, c_lim, hcd⟩ + let c' : ℕ → 𝕜 := fun n ↦ c n + refine ⟨c', d, d_mem, by simpa [c'] using c_lim, ?_⟩ + convert hcd using 2 with n + simp [c'] + +theorem UniqueDiffWithinAt.of_real (hs : UniqueDiffWithinAt ℝ s x) : + UniqueDiffWithinAt 𝕜 s x := by + refine ⟨?_, hs.mem_closure⟩ + letI : RCLike 𝕜 := h𝕜.rclike + apply hs.dense_tangentConeAt.mono + have : (Submodule.span ℝ (tangentConeAt ℝ s x) : Set E) + ⊆ (Submodule.span 𝕜 (tangentConeAt ℝ s x)) := Submodule.span_subset_span _ _ _ + exact this.trans (Submodule.span_mono tangentConeAt_real_subset_isRCLikeNormedField) + +theorem UniqueDiffOn.of_real (hs : UniqueDiffOn ℝ s) : + UniqueDiffOn 𝕜 s := + fun x hx ↦ (hs x hx).of_real + +/-- In a real or complex vector space, a convex set with nonempty interior is a set of unique +differentiability. -/ +theorem uniqueDiffWithinAt_convex_of_isRCLikeNormedField + (conv : Convex ℝ s) (hs : (interior s).Nonempty) (hx : x ∈ closure s) : + UniqueDiffWithinAt 𝕜 s x := + UniqueDiffWithinAt.of_real (uniqueDiffWithinAt_convex conv hs hx) + +/-- In a real or complex vector space, a convex set with nonempty interior is a set of unique +differentiability. -/ +theorem uniqueDiffOn_convex_of_isRCLikeNormedField + (conv : Convex ℝ s) (hs : (interior s).Nonempty) : UniqueDiffOn 𝕜 s := + UniqueDiffOn.of_real (uniqueDiffOn_convex conv hs) diff --git a/Mathlib/Analysis/SpecialFunctions/Log/Base.lean b/Mathlib/Analysis/SpecialFunctions/Log/Base.lean index 6fa49c5f0347a9..fb72ac2fc18512 100644 --- a/Mathlib/Analysis/SpecialFunctions/Log/Base.lean +++ b/Mathlib/Analysis/SpecialFunctions/Log/Base.lean @@ -64,11 +64,18 @@ lemma logb_self_eq_one_iff : logb b b = 1 ↔ b ≠ 0 ∧ b ≠ 1 ∧ b ≠ -1 : Iff.trans ⟨fun h h' => by simp [logb, h'] at h, div_self⟩ log_ne_zero @[simp] -theorem logb_abs (x : ℝ) : logb b |x| = logb b x := by rw [logb, logb, log_abs] +theorem logb_abs_base (b x : ℝ) : logb |b| x = logb b x := by rw [logb, logb, log_abs] @[simp] -theorem logb_neg_eq_logb (x : ℝ) : logb b (-x) = logb b x := by - rw [← logb_abs x, ← logb_abs (-x), abs_neg] +theorem logb_abs (b x : ℝ) : logb b |x| = logb b x := by rw [logb, logb, log_abs] + +@[simp] +theorem logb_neg_base_eq_logb (b x : ℝ) : logb (-b) x = logb b x := by + rw [← logb_abs_base b x, ← logb_abs_base (-b) x, abs_neg] + +@[simp] +theorem logb_neg_eq_logb (b x : ℝ) : logb b (-x) = logb b x := by + rw [← logb_abs b x, ← logb_abs b (-x), abs_neg] theorem logb_mul (hx : x ≠ 0) (hy : y ≠ 0) : logb b (x * y) = logb b x + logb b y := by simp_rw [logb, log_mul hx hy, add_div] @@ -77,7 +84,10 @@ theorem logb_div (hx : x ≠ 0) (hy : y ≠ 0) : logb b (x / y) = logb b x - log simp_rw [logb, log_div hx hy, sub_div] @[simp] -theorem logb_inv (x : ℝ) : logb b x⁻¹ = -logb b x := by simp [logb, neg_div] +theorem logb_inv (b x : ℝ) : logb b x⁻¹ = -logb b x := by simp [logb, neg_div] + +@[simp] +theorem logb_inv_base (b x : ℝ) : logb b⁻¹ x = -logb b x := by simp [logb, div_neg] theorem inv_logb (a b : ℝ) : (logb a b)⁻¹ = logb b a := by simp_rw [logb, inv_div] @@ -243,7 +253,7 @@ theorem strictMonoOn_logb : StrictMonoOn (logb b) (Set.Ioi 0) := fun _ hx _ _ hx theorem strictAntiOn_logb : StrictAntiOn (logb b) (Set.Iio 0) := by rintro x (hx : x < 0) y (hy : y < 0) hxy - rw [← logb_abs y, ← logb_abs x] + rw [← logb_abs b y, ← logb_abs b x] refine logb_lt_logb hb (abs_pos.2 hy.ne) ?_ rwa [abs_of_neg hy, abs_of_neg hx, neg_lt_neg_iff] @@ -324,7 +334,7 @@ theorem strictAntiOn_logb_of_base_lt_one : StrictAntiOn (logb b) (Set.Ioi 0) := theorem strictMonoOn_logb_of_base_lt_one : StrictMonoOn (logb b) (Set.Iio 0) := by rintro x (hx : x < 0) y (hy : y < 0) hxy - rw [← logb_abs y, ← logb_abs x] + rw [← logb_abs b y, ← logb_abs b x] refine logb_lt_logb_of_base_lt_one b_pos b_lt_one (abs_pos.2 hy.ne) ?_ rwa [abs_of_neg hy, abs_of_neg hx, neg_lt_neg_iff] @@ -444,6 +454,22 @@ lemma tendsto_logb_nhdsGT_zero_of_base_lt_one (hb₀ : 0 < b) (hb : b < 1) : @[deprecated (since := "2025-03-18")] alias tendsto_logb_nhdsWithin_zero_right_of_base_lt_one := tendsto_logb_nhdsGT_zero_of_base_lt_one +/-- +The function `|logb b x|` tends to `+∞` as `x` tendsto `+∞`. +See also `tendsto_logb_atTop` and `tendsto_logb_atTop_of_base_lt_one`. +-/ +lemma tendsto_abs_logb_atTop (hb : b ≠ -1 ∧ b ≠ 0 ∧ b ≠ 1) : + Tendsto (|logb b ·|) atTop atTop := by + wlog hb₀ : 0 < b generalizing b + · exact (this (b := -b) (by simp [hb, neg_eq_iff_eq_neg]) (by linarith +splitNe)).congr (by simp) + wlog hb₁ : 1 < b generalizing b + · exact (this (b := b⁻¹) (by simp [hb, inv_eq_iff_eq_inv, inv_neg]) (by simpa) + ((one_lt_inv₀ hb₀).2 (by linarith +splitNe))).congr (by simp) + refine (tendsto_logb_atTop hb₁).congr' ?_ + filter_upwards [eventually_ge_atTop 1] with x hx₁ + rw [abs_of_nonneg] + exact logb_nonneg hb₁ hx₁ + theorem continuousOn_logb : ContinuousOn (logb b) {0}ᶜ := continuousOn_log.div_const _ /-- The real logarithm base b is continuous as a function from nonzero reals. -/ @@ -473,11 +499,9 @@ theorem continuousAt_logb_iff (hb₀ : 0 < b) (hb : b ≠ 1) : ContinuousAt (log theorem logb_prod {α : Type*} (s : Finset α) (f : α → ℝ) (hf : ∀ x ∈ s, f x ≠ 0) : logb b (∏ i ∈ s, f i) = ∑ i ∈ s, logb b (f i) := by - classical - induction' s using Finset.induction_on with a s ha ih - · simp - simp only [Finset.mem_insert, forall_eq_or_imp] at hf - simp [ha, ih hf.2, logb_mul hf.1 (Finset.prod_ne_zero_iff.2 hf.2)] + induction s using Finset.cons_induction_on with + | empty => simp + | cons => simp_all [logb_mul, Finset.prod_ne_zero_iff] protected theorem _root_.Finsupp.logb_prod {α β : Type*} [Zero β] (f : α →₀ β) (g : α → β → ℝ) (hg : ∀ a, g a (f a) = 0 → f a = 0) : logb b (f.prod g) = f.sum fun a c ↦ logb b (g a c) := @@ -487,10 +511,83 @@ theorem logb_nat_eq_sum_factorization (n : ℕ) : logb b n = n.factorization.sum fun p t => t * logb b p := by simp only [logb, mul_div_assoc', log_nat_eq_sum_factorization n, Finsupp.sum, Finset.sum_div] --- TODO add other limits and continuous API lemmas analogous to those in Log.lean +theorem tendsto_pow_logb_div_mul_add_atTop (a c : ℝ) (n : ℕ) (ha : a ≠ 0) : + Tendsto (fun x => logb b x ^ n / (a * x + c)) atTop (𝓝 0) := by + cases eq_or_ne (log b) 0 with + | inl h => simpa [logb, h] using ((tendsto_mul_add_inv_atTop_nhds_zero _ _ ha).const_mul _) + | inr h => apply (tendsto_pow_log_div_mul_add_atTop (a * (log b) ^ n) (c * (log b) ^ n) n + (by positivity)).congr fun x ↦ by field_simp [logb]; ring + +theorem isLittleO_pow_logb_id_atTop {n : ℕ} : (fun x => logb b x ^ n) =o[atTop] id := by + rw [Asymptotics.isLittleO_iff_tendsto'] + · simpa using tendsto_pow_logb_div_mul_add_atTop 1 0 n one_ne_zero + · filter_upwards [eventually_ne_atTop (0 : ℝ)] with x h₁ h₂ using (h₁ h₂).elim + +theorem isLittleO_logb_id_atTop : logb b =o[atTop] id := + isLittleO_pow_logb_id_atTop.congr_left fun _ => pow_one _ + +theorem isLittleO_const_logb_atTop {c : ℝ} (hb : b ≠ -1 ∧ b ≠ 0 ∧ b ≠ 1) : + (fun _ => c) =o[atTop] logb b := by + rw [Asymptotics.isLittleO_const_left, or_iff_not_imp_left] + intro hc + exact tendsto_abs_logb_atTop hb + +end Real + +section Continuity + +open Real + +variable {α : Type*} +variable {b : ℝ} + +theorem Filter.Tendsto.logb {f : α → ℝ} {l : Filter α} {x : ℝ} + (h : Tendsto f l (𝓝 x)) (hx : x ≠ 0) : + Tendsto (fun y => logb b (f y)) l (𝓝 (logb b x)) := + (continuousAt_logb hx).tendsto.comp h + +variable [TopologicalSpace α] {f : α → ℝ} {s : Set α} {a : α} + +@[fun_prop] +theorem Continuous.logb (hf : Continuous f) (h₀ : ∀ x, f x ≠ 0) : + Continuous fun x => logb b (f x) := + continuousOn_logb.comp_continuous hf h₀ + +@[fun_prop] +nonrec theorem ContinuousAt.logb (hf : ContinuousAt f a) (h₀ : f a ≠ 0) : + ContinuousAt (fun x => logb b (f x)) a := + hf.logb h₀ + +nonrec theorem ContinuousWithinAt.logb (hf : ContinuousWithinAt f s a) (h₀ : f a ≠ 0) : + ContinuousWithinAt (fun x => logb b (f x)) s a := + hf.logb h₀ + +@[fun_prop] +theorem ContinuousOn.logb (hf : ContinuousOn f s) (h₀ : ∀ x ∈ s, f x ≠ 0) : + ContinuousOn (fun x => logb b (f x)) s := fun x hx => (hf x hx).logb (h₀ x hx) + +end Continuity + +section TendstoCompAddSub + +open Filter + +namespace Real + +variable {b : ℝ} + +theorem tendsto_logb_comp_add_sub_logb (y : ℝ) : + Tendsto (fun x : ℝ => logb b (x + y) - logb b x) atTop (𝓝 0) := by + simpa [sub_div] using (tendsto_log_comp_add_sub_log y).div_const (log b) + +theorem tendsto_logb_nat_add_one_sub_logb : + Tendsto (fun k : ℕ => logb b (k + 1) - logb b k) atTop (𝓝 0) := + (tendsto_logb_comp_add_sub_logb 1).comp tendsto_natCast_atTop_atTop end Real +end TendstoCompAddSub + section Induction /-- Induction principle for intervals of real numbers: if a proposition `P` is true diff --git a/Mathlib/Analysis/SpecificLimits/Basic.lean b/Mathlib/Analysis/SpecificLimits/Basic.lean index a2b23895ce9ae9..0931d40212eb2d 100644 --- a/Mathlib/Analysis/SpecificLimits/Basic.lean +++ b/Mathlib/Analysis/SpecificLimits/Basic.lean @@ -100,6 +100,15 @@ theorem tendsto_mod_div_atTop_nhds_zero_nat {m : ℕ} (hm : 0 < m) : exact tendsto_bdd_div_atTop_nhds_zero h0 (.of_forall (fun n ↦ cast_le.mpr (mod_lt n hm).le)) tendsto_natCast_atTop_atTop +/-- If `a ≠ 0`, `(a * x + c)⁻¹` tends to `0` as `x` tends to `∞`. -/ +theorem tendsto_mul_add_inv_atTop_nhds_zero (a c : ℝ) (ha : a ≠ 0) : + Tendsto (fun x => (a * x + c)⁻¹) atTop (𝓝 0) := by + obtain ha' | ha' := lt_or_gt_of_ne ha + · exact tendsto_inv_atBot_zero.comp + (tendsto_atBot_add_const_right _ c (tendsto_id.const_mul_atTop_of_neg ha')) + · exact tendsto_inv_atTop_zero.comp + (tendsto_atTop_add_const_right _ c (tendsto_id.const_mul_atTop ha')) + theorem Filter.EventuallyEq.div_mul_cancel {α G : Type*} [GroupWithZero G] {f g : α → G} {l : Filter α} (hg : Tendsto g l (𝓟 {0}ᶜ)) : (fun x ↦ f x / g x * g x) =ᶠ[l] fun x ↦ f x := by filter_upwards [hg.le_comap <| preimage_mem_comap (m := g) (mem_principal_self {0}ᶜ)] with x hx diff --git a/Mathlib/CategoryTheory/Abelian/Images.lean b/Mathlib/CategoryTheory/Abelian/Images.lean index 0b4b6a16873afe..1bae2061da5ab2 100644 --- a/Mathlib/CategoryTheory/Abelian/Images.lean +++ b/Mathlib/CategoryTheory/Abelian/Images.lean @@ -32,11 +32,13 @@ open CategoryTheory.Limits namespace CategoryTheory.Abelian -variable {C : Type u} [Category.{v} C] [HasZeroMorphisms C] [HasKernels C] [HasCokernels C] +variable {C : Type u} [Category.{v} C] [HasZeroMorphisms C] variable {P Q : C} (f : P ⟶ Q) section Image +variable [HasCokernel f] [HasKernel (cokernel.π f)] + /-- The kernel of the cokernel of `f` is called the (abelian) image of `f`. -/ protected abbrev image : C := kernel (cokernel.π f) @@ -60,6 +62,8 @@ end Image section Coimage +variable [HasKernel f] [HasCokernel (kernel.ι f)] + /-- The cokernel of the kernel of `f` is called the (abelian) coimage of `f`. -/ protected abbrev coimage : C := cokernel (kernel.ι f) @@ -81,6 +85,10 @@ instance epi_factorThruCoimage [Epi f] : Epi (Abelian.factorThruCoimage f) := end Coimage +section Comparison + +variable [HasCokernel f] [HasKernel f] [HasKernel (cokernel.π f)] [HasCokernel (kernel.ι f)] + /-- The canonical map from the abelian coimage to the abelian image. In any abelian category this is an isomorphism. @@ -104,6 +112,10 @@ theorem coimageImageComparison_eq_coimageImageComparison' : theorem coimage_image_factorisation : coimage.π f ≫ coimageImageComparison f ≫ image.ι f = f := by simp [coimageImageComparison] +end Comparison + +variable [HasKernels C] [HasCokernels C] + /-- The coimage-image comparison morphism is functorial. -/ @[simps! obj map] def coimageImageComparisonFunctor : Arrow C ⥤ Arrow C where diff --git a/Mathlib/CategoryTheory/Abelian/Indization.lean b/Mathlib/CategoryTheory/Abelian/Indization.lean index 27fc0e6ce51847..b1598059f0fa8d 100644 --- a/Mathlib/CategoryTheory/Abelian/Indization.lean +++ b/Mathlib/CategoryTheory/Abelian/Indization.lean @@ -28,6 +28,7 @@ instance {X Y : Ind C} (f : X ⟶ Y) : IsIso (Abelian.coimageImageComparison f) obtain ⟨I, _, _, F, G, ϕ, ⟨i⟩⟩ := Ind.exists_nonempty_arrow_mk_iso_ind_lim (f := f) let i' := coimageImageComparisonFunctor.mapIso i dsimp only [coimageImageComparisonFunctor_obj, Arrow.mk_left, Arrow.mk_right, Arrow.mk_hom] at i' + have := Iso.isIso_hom i' rw [Arrow.isIso_iff_isIso_of_isIso i'.hom, Arrow.isIso_iff_isIso_of_isIso (PreservesCoimageImageComparison.iso (Ind.lim I) ϕ).inv] infer_instance diff --git a/Mathlib/CategoryTheory/Bicategory/Functor/LocallyDiscrete.lean b/Mathlib/CategoryTheory/Bicategory/Functor/LocallyDiscrete.lean index 17e17f0c9bb8f7..41ba95251af045 100644 --- a/Mathlib/CategoryTheory/Bicategory/Functor/LocallyDiscrete.lean +++ b/Mathlib/CategoryTheory/Bicategory/Functor/LocallyDiscrete.lean @@ -100,6 +100,32 @@ def oplaxFunctorOfIsLocallyDiscrete section +variable {C D : Type*} [Category C] [Category D] (F : C ⥤ D) + +/-- +A functor between two categories `C` and `D` can be lifted to a pseudofunctor between the +corresponding locally discrete bicategories. +-/ +@[simps! obj map mapId mapComp] +def Functor.toPseudoFunctor : Pseudofunctor (LocallyDiscrete C) (LocallyDiscrete D) := + pseudofunctorOfIsLocallyDiscrete + (fun ⟨X⟩ ↦.mk <| F.obj X) (fun ⟨f⟩ ↦ (F.map f).toLoc) + (fun ⟨X⟩ ↦ eqToIso (by simp)) (fun f g ↦ eqToIso (by simp)) + +/-- +A functor between two categories `C` and `D` can be lifted to an oplax functor between the +corresponding locally discrete bicategories. + +This is just an abbreviation of `Functor.toPseudoFunctor.toOplax`. +-/ +@[simps! obj map mapId mapComp] +abbrev Functor.toOplaxFunctor : OplaxFunctor (LocallyDiscrete C) (LocallyDiscrete D) := + F.toPseudoFunctor.toOplax + +end + +section + variable {I B : Type*} [Category I] [Bicategory B] [Strict B] (F : I ⥤ B) attribute [local simp] @@ -110,7 +136,7 @@ If `B` is a strict bicategory and `I` is a (1-)category, any functor (of 1-categ be promoted to a pseudofunctor from `LocallyDiscrete I` to `B`. -/ @[simps! obj map mapId mapComp] -def Functor.toPseudoFunctor : Pseudofunctor (LocallyDiscrete I) B := +def Functor.toPseudoFunctor' : Pseudofunctor (LocallyDiscrete I) B := pseudofunctorOfIsLocallyDiscrete (fun ⟨X⟩ ↦ F.obj X) (fun ⟨f⟩ ↦ F.map f) (fun ⟨X⟩ ↦ eqToIso (by simp)) (fun f g ↦ eqToIso (by simp)) @@ -120,10 +146,8 @@ If `B` is a strict bicategory and `I` is a (1-)category, any functor (of 1-categ be promoted to an oplax functor from `LocallyDiscrete I` to `B`. -/ @[simps! obj map mapId mapComp] -def Functor.toOplaxFunctor : OplaxFunctor (LocallyDiscrete I) B := - oplaxFunctorOfIsLocallyDiscrete - (fun ⟨X⟩ ↦ F.obj X) (fun ⟨f⟩ ↦ F.map f) - (fun ⟨X⟩ ↦ eqToHom (by simp)) (fun f g ↦ eqToHom (by simp)) +abbrev Functor.toOplaxFunctor' : OplaxFunctor (LocallyDiscrete I) B := + F.toPseudoFunctor'.toOplax end diff --git a/Mathlib/CategoryTheory/Category/Bipointed.lean b/Mathlib/CategoryTheory/Category/Bipointed.lean index 81bff7c80315c0..aa1c056363b75d 100644 --- a/Mathlib/CategoryTheory/Category/Bipointed.lean +++ b/Mathlib/CategoryTheory/Category/Bipointed.lean @@ -195,8 +195,7 @@ def pointedToBipointedFstBipointedToPointedFstAdjunction : funext x cases x · exact f.map_snd.symm - · rfl - right_inv := fun _ => Pointed.Hom.ext rfl } + · rfl } homEquiv_naturality_left_symm := fun f g => by apply Bipointed.Hom.ext funext x @@ -215,8 +214,7 @@ def pointedToBipointedSndBipointedToPointedSndAdjunction : funext x cases x · exact f.map_fst.symm - · rfl - right_inv := fun _ => Pointed.Hom.ext rfl } + · rfl } homEquiv_naturality_left_symm := fun f g => by apply Bipointed.Hom.ext funext x diff --git a/Mathlib/CategoryTheory/Category/Cat/Adjunction.lean b/Mathlib/CategoryTheory/Category/Cat/Adjunction.lean index 43e4632a7ed623..17a321a0ee0581 100644 --- a/Mathlib/CategoryTheory/Category/Cat/Adjunction.lean +++ b/Mathlib/CategoryTheory/Category/Cat/Adjunction.lean @@ -32,7 +32,6 @@ private def typeToCatObjectsAdjHomEquiv : (typeToCat.obj X ⟶ C) ≃ (X ⟶ Cat left_inv F := Functor.ext (fun _ ↦ rfl) (fun ⟨_⟩ ⟨_⟩ f => by obtain rfl := Discrete.eq_of_hom f simp) - right_inv _ := rfl private def typeToCatObjectsAdjCounitApp : (Cat.objects ⋙ typeToCat).obj C ⥤ C where obj := Discrete.as diff --git a/Mathlib/CategoryTheory/Category/Pointed.lean b/Mathlib/CategoryTheory/Category/Pointed.lean index 271cfa73611cc4..4a63d6f542da10 100644 --- a/Mathlib/CategoryTheory/Category/Pointed.lean +++ b/Mathlib/CategoryTheory/Category/Pointed.lean @@ -119,8 +119,7 @@ def typeToPointedForgetAdjunction : typeToPointed ⊣ forget Pointed := funext x cases x · exact f.map_point.symm - · rfl - right_inv := fun _ => funext fun _ => rfl } + · rfl } homEquiv_naturality_left_symm := fun f g => by apply Pointed.Hom.ext funext x diff --git a/Mathlib/CategoryTheory/Category/Quiv.lean b/Mathlib/CategoryTheory/Category/Quiv.lean index 3f9d41ac0926df..0d023a051e8990 100644 --- a/Mathlib/CategoryTheory/Category/Quiv.lean +++ b/Mathlib/CategoryTheory/Category/Quiv.lean @@ -89,7 +89,7 @@ def freeMapIdIso (V : Type*) [Quiver V] : freeMap (𝟭q V) ≅ 𝟭 _ := theorem freeMap_id (V : Type*) [Quiver V] : freeMap (𝟭q V) = 𝟭 _ := - Functor.ext_of_iso (freeMapIdIso V) (fun _ ↦ rfl) (fun _ ↦ rfl) + Functor.ext_of_iso (freeMapIdIso V) (fun _ ↦ rfl) /-- The functor `free : Quiv ⥤ Cat` preserves composition up to natural isomorphism and in fact up to equality. -/ @@ -105,8 +105,7 @@ theorem freeMap_comp {V₁ : Type u₁} {V₂ : Type u₂} {V₃ : Type u₃} [Quiver.{v₁ + 1} V₁] [Quiver.{v₂ + 1} V₂] [Quiver.{v₃ + 1} V₃] (F : V₁ ⥤q V₂) (G : V₂ ⥤q V₃) : freeMap (F ⋙q G) = freeMap F ⋙ freeMap G := - Functor.ext_of_iso (freeMapCompIso F G) - (fun _ ↦ rfl) (fun _ ↦ rfl) + Functor.ext_of_iso (freeMapCompIso F G) (fun _ ↦ rfl) /-- The functor sending each quiver to its path category. -/ @[simps] diff --git a/Mathlib/CategoryTheory/Category/TwoP.lean b/Mathlib/CategoryTheory/Category/TwoP.lean index 833835efb2cebf..6606e537f2f80d 100644 --- a/Mathlib/CategoryTheory/Category/TwoP.lean +++ b/Mathlib/CategoryTheory/Category/TwoP.lean @@ -140,8 +140,7 @@ noncomputable def pointedToTwoPFstForgetCompBipointedToPointedFstAdjunction : funext x cases x · exact f.map_snd.symm - · rfl - right_inv := fun _ => Pointed.Hom.ext rfl } + · rfl } homEquiv_naturality_left_symm := fun f g => by apply Bipointed.Hom.ext funext x @@ -159,8 +158,7 @@ noncomputable def pointedToTwoPSndForgetCompBipointedToPointedSndAdjunction : funext x cases x · exact f.map_fst.symm - · rfl - right_inv := fun _ => Pointed.Hom.ext rfl } + · rfl } homEquiv_naturality_left_symm := fun f g => by apply Bipointed.Hom.ext funext x diff --git a/Mathlib/CategoryTheory/CodiscreteCategory.lean b/Mathlib/CategoryTheory/CodiscreteCategory.lean index 3aeea7038fae5f..0d50d32fe1f9c0 100644 --- a/Mathlib/CategoryTheory/CodiscreteCategory.lean +++ b/Mathlib/CategoryTheory/CodiscreteCategory.lean @@ -116,8 +116,6 @@ def equivFunctorToCodiscrete {C : Type u} [Category.{v} C] {A : Type w} : (C → A) ≃ (C ⥤ Codiscrete A) where toFun := functor invFun := invFunctor - left_inv _ := rfl - right_inv _ := rfl /-- The functor that turns a type into a codiscrete category is right adjoint to the objects functor. -/ diff --git a/Mathlib/CategoryTheory/CofilteredSystem.lean b/Mathlib/CategoryTheory/CofilteredSystem.lean index f46a9dc2c9f075..36fc002dc54556 100644 --- a/Mathlib/CategoryTheory/CofilteredSystem.lean +++ b/Mathlib/CategoryTheory/CofilteredSystem.lean @@ -265,12 +265,6 @@ def toEventualRangesSectionsEquiv : F.toEventualRanges.sections ≃ F.sections w toFun s := ⟨_, fun f => Subtype.coe_inj.2 <| s.prop f⟩ invFun s := ⟨fun _ => ⟨_, mem_iInter₂.2 fun _ f => ⟨_, s.prop f⟩⟩, fun f => Subtype.ext <| s.prop f⟩ - left_inv _ := by - ext - rfl - right_inv _ := by - ext - rfl /-- If `F` satisfies the Mittag-Leffler condition, its restriction to eventual ranges is a surjective functor. -/ diff --git a/Mathlib/CategoryTheory/Comma/Arrow.lean b/Mathlib/CategoryTheory/Comma/Arrow.lean index 481504032b5492..f1a65044ff21ac 100644 --- a/Mathlib/CategoryTheory/Comma/Arrow.lean +++ b/Mathlib/CategoryTheory/Comma/Arrow.lean @@ -385,8 +385,6 @@ def Arrow.equivSigma : Arrow T ≃ Σ (X Y : T), X ⟶ Y where toFun f := ⟨_, _, f.hom⟩ invFun x := Arrow.mk x.2.2 - left_inv _ := rfl - right_inv _ := rfl /-- The equivalence `Arrow (Discrete S) ≃ S`. -/ def Arrow.discreteEquiv (S : Type u) : Arrow (Discrete S) ≃ S where @@ -396,7 +394,6 @@ def Arrow.discreteEquiv (S : Type u) : Arrow (Discrete S) ≃ S where rintro ⟨⟨_⟩, ⟨_⟩, f⟩ obtain rfl := Discrete.eq_of_hom f rfl - right_inv _ := rfl /-- Extensionality lemma for functors `C ⥤ D` which uses as an assumption that the induced maps `Arrow C → Arrow D` coincide. -/ diff --git a/Mathlib/CategoryTheory/Comma/CardinalArrow.lean b/Mathlib/CategoryTheory/Comma/CardinalArrow.lean index cb459b8a6f8622..0d4e57798f5bd5 100644 --- a/Mathlib/CategoryTheory/Comma/CardinalArrow.lean +++ b/Mathlib/CategoryTheory/Comma/CardinalArrow.lean @@ -48,8 +48,6 @@ instance Arrow.finite {C : Type u} [SmallCategory C] [FinCategory C] : def Arrow.opEquiv (C : Type u) [Category.{v} C] : Arrow Cᵒᵖ ≃ Arrow C where toFun f := Arrow.mk f.hom.unop invFun g := Arrow.mk g.hom.op - left_inv _ := rfl - right_inv _ := rfl @[simp] lemma hasCardinalLT_arrow_op_iff (C : Type u) [Category.{v} C] (κ : Cardinal.{w}) : diff --git a/Mathlib/CategoryTheory/Comma/Over/Basic.lean b/Mathlib/CategoryTheory/Comma/Over/Basic.lean index ef93c70240e040..161fe9bf6dc5ce 100644 --- a/Mathlib/CategoryTheory/Comma/Over/Basic.lean +++ b/Mathlib/CategoryTheory/Comma/Over/Basic.lean @@ -123,6 +123,10 @@ lemma inv_left_hom_left {f g : Over X} (e : f ≅ g) : e.inv.left ≫ e.hom.left = 𝟙 g.left := by simp [← Over.comp_left] +lemma forall_iff (P : Over X → Prop) : + (∀ Y, P Y) ↔ (∀ (Y) (f : Y ⟶ X), P (.mk f)) := by + aesop + section variable (X) @@ -535,6 +539,10 @@ lemma inv_right_hom_right {f g : Under X} (e : f ≅ g) : e.inv.right ≫ e.hom.right = 𝟙 g.right := by simp [← Under.comp_right] +lemma forall_iff (P : Under X → Prop) : + (∀ Y, P Y) ↔ (∀ (Y) (f : X ⟶ Y), P (.mk f)) := by + aesop + section variable (X) diff --git a/Mathlib/CategoryTheory/ComposableArrows.lean b/Mathlib/CategoryTheory/ComposableArrows.lean index 4ef596acb88da3..d4a53aa31e76e2 100644 --- a/Mathlib/CategoryTheory/ComposableArrows.lean +++ b/Mathlib/CategoryTheory/ComposableArrows.lean @@ -204,7 +204,7 @@ lemma ext {F G : ComposableArrows C n} (h : ∀ i, F.obj i = G.obj i) (w : ∀ (i : ℕ) (hi : i < n), F.map' i (i + 1) = eqToHom (h _) ≫ G.map' i (i + 1) ≫ eqToHom (h _).symm) : F = G := Functor.ext_of_iso - (isoMk (fun i => eqToIso (h i)) (fun i hi => by simp [w i hi])) h (fun _ => rfl) + (isoMk (fun i => eqToIso (h i)) (fun i hi => by simp [w i hi])) h /-- Constructor for morphisms in `ComposableArrows C 0`. -/ @[simps!] diff --git a/Mathlib/CategoryTheory/Endomorphism.lean b/Mathlib/CategoryTheory/Endomorphism.lean index ccf7177556b159..989de0927176c1 100644 --- a/Mathlib/CategoryTheory/Endomorphism.lean +++ b/Mathlib/CategoryTheory/Endomorphism.lean @@ -136,8 +136,6 @@ are (multiplicatively) equivalent to automorphisms of that object. def unitsEndEquivAut : (End X)ˣ ≃* Aut X where toFun f := ⟨f.1, f.2, f.4, f.3⟩ invFun f := ⟨f.1, f.2, f.4, f.3⟩ - left_inv := fun ⟨_, _, _, _⟩ => rfl - right_inv := fun ⟨_, _, _, _⟩ => rfl map_mul' f g := by cases f; cases g; rfl /-- The inclusion of `Aut X` to `End X` as a monoid homomorphism. -/ diff --git a/Mathlib/CategoryTheory/Enriched/Basic.lean b/Mathlib/CategoryTheory/Enriched/Basic.lean index 95bf36525c56d8..7ef5b4322473fa 100644 --- a/Mathlib/CategoryTheory/Enriched/Basic.lean +++ b/Mathlib/CategoryTheory/Enriched/Basic.lean @@ -163,8 +163,6 @@ def enrichedCategoryTypeEquivCategory (C : Type u₁) : EnrichedCategory (Type v) C ≃ Category.{v} C where toFun _ := categoryOfEnrichedCategoryType C invFun _ := enrichedCategoryTypeOfCategory C - left_inv _ := rfl - right_inv _ := rfl section @@ -438,8 +436,6 @@ def enrichedFunctorTypeEquivFunctor {C : Type u₁} [𝒞 : EnrichedCategory (Ty map := fun _ _ f => F.map f map_id := fun X => by ext ⟨⟩; exact F.map_id X map_comp := fun X Y Z => by ext ⟨f, g⟩; exact F.map_comp f g } - left_inv _ := rfl - right_inv _ := rfl /-- We verify that the presheaf representing natural transformations between `Type v`-enriched functors is actually represented by diff --git a/Mathlib/CategoryTheory/EqToHom.lean b/Mathlib/CategoryTheory/EqToHom.lean index 1d954ff440663e..cef968bfb2342c 100644 --- a/Mathlib/CategoryTheory/EqToHom.lean +++ b/Mathlib/CategoryTheory/EqToHom.lean @@ -250,7 +250,7 @@ theorem ext {F G : C ⥤ D} (h_obj : ∀ X, F.obj X = G.obj X) simpa using h_map X Y f lemma ext_of_iso {F G : C ⥤ D} (e : F ≅ G) (hobj : ∀ X, F.obj X = G.obj X) - (happ : ∀ X, e.hom.app X = eqToHom (hobj X)) : F = G := + (happ : ∀ X, e.hom.app X = eqToHom (hobj X) := by aesop_cat) : F = G := Functor.ext hobj (fun X Y f => by rw [← cancel_mono (e.hom.app Y), e.hom.naturality f, happ, happ, Category.assoc, Category.assoc, eqToHom_trans, eqToHom_refl, Category.comp_id]) diff --git a/Mathlib/CategoryTheory/FiberedCategory/Cartesian.lean b/Mathlib/CategoryTheory/FiberedCategory/Cartesian.lean index 6361c2bc5a0f3c..8481d4d64bd04c 100644 --- a/Mathlib/CategoryTheory/FiberedCategory/Cartesian.lean +++ b/Mathlib/CategoryTheory/FiberedCategory/Cartesian.lean @@ -117,7 +117,18 @@ lemma map_self : IsCartesian.map p f φ φ = 𝟙 a := by apply map_uniq simp only [id_comp] -/-- The canonical isomorphism between the domains of two cartesian morphisms +instance of_comp_iso {b' : 𝒳} (φ' : b ≅ b') [IsHomLift p (𝟙 S) φ'.hom] : + IsCartesian p f (φ ≫ φ'.hom) where + universal_property := by + intro c ψ hψ + use IsCartesian.map p f φ (ψ ≫ φ'.inv) + refine ⟨⟨inferInstance, by simp only [fac_assoc, assoc, Iso.inv_hom_id, comp_id]⟩, ?_⟩ + rintro τ ⟨hτ₁, hτ₂⟩ + apply map_uniq + rw [Iso.eq_comp_inv] + simp only [assoc, hτ₂] + +/-- The canonical isomorphism between the domains of two cartesian arrows lying over the same object. -/ @[simps] noncomputable def domainUniqueUpToIso {a' : 𝒳} (φ' : a' ⟶ b) [IsCartesian p f φ'] : a' ≅ a where @@ -152,17 +163,6 @@ instance of_iso_comp {a' : 𝒳} (φ' : a' ≅ a) [IsHomLift p (𝟙 R) φ'.hom] apply map_uniq simp only [assoc, hτ₂] -/-- Postcomposing a cartesian morphism with an isomorphism lifting the identity is cartesian. -/ -instance of_comp_iso {b' : 𝒳} (φ' : b ≅ b') [IsHomLift p (𝟙 S) φ'.hom] : - IsCartesian p f (φ ≫ φ'.hom) where - universal_property := by - intro c ψ hψ - use IsCartesian.map p f φ (ψ ≫ φ'.inv) - refine ⟨⟨inferInstance, by simp⟩, ?_⟩ - rintro τ ⟨hτ₁, hτ₂⟩ - apply map_uniq - simp only [Iso.eq_comp_inv, assoc, hτ₂] - end IsCartesian namespace IsStronglyCartesian diff --git a/Mathlib/CategoryTheory/FiberedCategory/Fiber.lean b/Mathlib/CategoryTheory/FiberedCategory/Fiber.lean index 64fc16cf64dea2..c72b9a6007d7bf 100644 --- a/Mathlib/CategoryTheory/FiberedCategory/Fiber.lean +++ b/Mathlib/CategoryTheory/FiberedCategory/Fiber.lean @@ -65,7 +65,7 @@ def fiberInclusionCompIsoConst : fiberInclusion ⋙ p ≅ (const (Fiber p S)).ob (fun φ ↦ by simp [IsHomLift.fac' p (𝟙 S) (fiberInclusion.map φ)]) lemma fiberInclusion_comp_eq_const : fiberInclusion ⋙ p = (const (Fiber p S)).obj S := - Functor.ext (fun x ↦ x.2) (fun _ _ φ ↦ IsHomLift.fac' p (𝟙 S) (fiberInclusion.map φ)) + Functor.ext_of_iso fiberInclusionCompIsoConst (fun x ↦ x.2) /-- The object of the fiber over `S` corresponding to a `a : 𝒳` such that `p(a) = S`. -/ def mk {p : 𝒳 ⥤ 𝒮} {S : 𝒮} {a : 𝒳} (ha : p.obj a = S) : Fiber p S := ⟨a, ha⟩ diff --git a/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean b/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean new file mode 100644 index 00000000000000..99650ad413555f --- /dev/null +++ b/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean @@ -0,0 +1,239 @@ +/- +Copyright (c) 2024 Calle Sönne. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Calle Sönne, Paul Lezeau +-/ +import Mathlib.CategoryTheory.FiberedCategory.Fiber +import Mathlib.CategoryTheory.FiberedCategory.Fibered + +/-! + +# Fibers of functors + +In this file we introduce a typeclass `HasFibers` for a functor `p : 𝒳 ⥤ 𝒮`, consisting of: +- A collection of categories `Fib S` for every `S` in `𝒮` (the fiber categories) +- Functors `ι : Fib S ⥤ 𝒳` such that `ι ⋙ p = const (Fib S) S +- The induced functor `Fib S ⥤ Fiber p S` is an equivalence. + +We also provide a canonical `HasFibers` instance, which uses the standard fibers `Fiber p S` +(see Fiber.lean). This makes it so that any result proven about `HasFibers` can be used for the +standard fibers as well. + +The reason for introducing this typeclass is that in practice, when working with (pre)fibered +categories one often already has a collection of categories `Fib S` for every `S` that are +equivalent to the fibers `Fiber p S`. One would then like to use these categories `Fib S` directly, +instead of working through this equivalence of categories. By developing an API for the `HasFibers` +typeclass, this will be possible. + +Here is an example of when this typeclass is useful. Suppose we have a presheaf of types +`F : 𝒮ᵒᵖ ⥤ Type _`. The associated fibered category then has objects `(S, a)` where `S : 𝒮` and `a` +is an element of `F(S)`. The fiber category `Fiber p S` is then equivalent to the discrete category +`Fib S` with objects `a` in `F(S)`. In this case, the `HasFibers` instance is given by the +categories `F(S)` and the functor `ι` sends `a : F(S)` to `(S, a)` in the fibered category. + +## Main API +The following API is developed so that the fibers from a `HasFibers` instance can be used +analogously to the standard fibers. + +- `Fib.homMk φ` is a lift of a morphism `φ : (ι S).obj a ⟶ (ι S).obj b` in `𝒳`, which lies over +`𝟙 S`, to a morphism in the fiber over `S`. +- `Fib.mk` gives an object in the fiber over `S` which is isomorphic to a given `a : 𝒳` that +satisfies `p(a) = S`. The isomorphism is given by `Fib.mkIsoSelf`. +- `HasFibers.mkPullback` is a version of `IsPreFibered.mkPullback` which ensures that the object +lies in a given fiber. The corresponding cartesian morphism is given by `HasFibers.pullbackMap`. +- `HasFibers.inducedMap` is a version of `IsCartesian.inducedMap` which gives the corresponding +morphism in the fiber category. +- `fiber_factorization` is the statement that any morphism in `𝒳` can be factored as a morphism in +some fiber followed by a pullback. + +-/ + +universe v₃ u₃ v₂ u₂ v₁ u₁ + +open CategoryTheory Functor Category IsCartesian IsHomLift Fiber + +variable {𝒮 : Type u₁} {𝒳 : Type u₂} [Category.{v₁} 𝒮] [Category.{v₂} 𝒳] + +/-- HasFibers is an extrinsic notion of fibers on a functor `p : 𝒳 ⥤ 𝒮`. It is given by a +collection of categories `Fib S` for every `S : 𝒮` (the fiber categories), each equipped with a +functors `ι : Fib S ⥤ 𝒳` which map constantly to `S` on the base such that the induced functor +`Fib S ⥤ Fiber p S` is an equivalence. -/ +@[nolint checkUnivs] +class HasFibers (p : 𝒳 ⥤ 𝒮) where + /-- The type of objects of the category `Fib S` for each `S`. -/ + Fib (S : 𝒮) : Type u₃ + /-- `Fib S` is a category. -/ + category (S : 𝒮) : Category.{v₃} (Fib S) := by infer_instance + /-- The functor `ι : Fib S ⥤ 𝒳`. -/ + ι (S : 𝒮) : Fib S ⥤ 𝒳 + /-- The composition with the functor `p` is *equal* to the constant functor mapping to `S`. -/ + comp_const (S : 𝒮) : ι S ⋙ p = (const (Fib S)).obj S + /-- The induced functor from `Fib S` to the fiber of `𝒳 ⥤ 𝒮` over `S` is an equivalence. -/ + equiv (S : 𝒮) : Functor.IsEquivalence (inducedFunctor (comp_const S)) + +namespace HasFibers + +/-- The `HasFibers` on `p : 𝒳 ⥤ 𝒮` given by the fibers of `p` -/ +def canonical (p : 𝒳 ⥤ 𝒮) : HasFibers p where + Fib := Fiber p + ι S := fiberInclusion + comp_const S := fiberInclusion_comp_eq_const + equiv S := by exact isEquivalence_of_iso (F := 𝟭 (Fiber p S)) (Iso.refl _) + +section + +variable (p : 𝒳 ⥤ 𝒮) [HasFibers p] (S : 𝒮) + +attribute [instance] category + +/-- The induced functor from `Fib p S` to the standard fiber. -/ +@[simps!] +def inducedFunctor : Fib p S ⥤ Fiber p S := + Fiber.inducedFunctor (comp_const S) + +/-- The natural transformation `ι S ≅ (inducedFunctor p S) ⋙ (fiberInclusion p S)` -/ +def inducedFunctor.natIso : ι S ≅ (inducedFunctor p S) ⋙ fiberInclusion := + Fiber.inducedFunctorCompIsoSelf (comp_const S) + +lemma inducedFunctor_comp : ι S = (inducedFunctor p S) ⋙ fiberInclusion := + Fiber.inducedFunctor_comp (comp_const S) + +instance : Functor.IsEquivalence (inducedFunctor p S) := equiv S + +instance : Functor.Faithful (ι (p:=p) S) := + Functor.Faithful.of_iso (inducedFunctor.natIso p S).symm + +end + +section + +variable {p : 𝒳 ⥤ 𝒮} [HasFibers p] + +@[simp] +lemma proj_eq {S : 𝒮} (a : Fib p S) : p.obj ((ι S).obj a) = S := by + simp only [← comp_obj, comp_const, const_obj_obj] + +/-- The morphism `R ⟶ S` in `𝒮` obtained by projecting a morphism +`φ : (ι R).obj a ⟶ (ι S).obj b`. -/ +def projMap {R S : 𝒮} {a : Fib p R} {b : Fib p S} + (φ : (ι R).obj a ⟶ (ι S).obj b) : R ⟶ S := + eqToHom (proj_eq a).symm ≫ (p.map φ) ≫ eqToHom (proj_eq b) + +/-- For any homomorphism `φ` in a fiber `Fib S`, its image under `ι S` lies over `𝟙 S`. -/ +instance homLift {S : 𝒮} {a b : Fib p S} (φ : a ⟶ b) : IsHomLift p (𝟙 S) ((ι S).map φ) := by + apply of_fac p _ _ (proj_eq a) (proj_eq b) + rw [← Functor.comp_map, Functor.congr_hom (comp_const S)] + simp + +/-- A version of fullness of the functor `Fib S ⥤ Fiber p S` that can be used inside the category +`𝒳`. -/ +noncomputable def Fib.homMk {S : 𝒮} {a b : Fib p S} (φ : (ι S).obj a ⟶ (ι S).obj b) + [IsHomLift p (𝟙 S) φ] : a ⟶ b := + (inducedFunctor _ S).preimage (Fiber.homMk p S φ) + +@[simp] +lemma Fib.map_homMk {S : 𝒮} {a b : Fib p S} (φ : (ι S).obj a ⟶ (ι S).obj b) + [IsHomLift p (𝟙 S) φ] : (ι S).map (homMk φ) = φ := by + simp [Fib.homMk, congr_hom (inducedFunctor_comp p S)] + +@[ext] +lemma Fib.hom_ext {S : 𝒮} {a b : Fib p S} {f g : a ⟶ b} + (h : (ι S).map f = (ι S).map g) : f = g := + (ι S).map_injective h + +/-- The lift of an isomorphism `Φ : (ι S).obj a ≅ (ι S).obj b` lying over `𝟙 S` to an isomorphism +in `Fib S`. -/ +@[simps] +noncomputable def Fib.isoMk {S : 𝒮} {a b : Fib p S} + (Φ : (ι S).obj a ≅ (ι S).obj b) (hΦ : IsHomLift p (𝟙 S) Φ.hom) : a ≅ b where + hom := Fib.homMk Φ.hom + inv := Fib.homMk Φ.inv + +/-- An object in `Fib p S` isomorphic in `𝒳` to a given object `a : 𝒳` such that `p(a) = S`. -/ +noncomputable def Fib.mk {S : 𝒮} {a : 𝒳} (ha : p.obj a = S) : Fib p S := + Functor.objPreimage (inducedFunctor p S) (Fiber.mk ha) + +/-- Applying `ι S` to the preimage of `a : 𝒳` in `Fib p S` yields an object isomorphic to `a`. -/ +noncomputable def Fib.mkIsoSelf {S : 𝒮} {a : 𝒳} (ha : p.obj a = S) : + (ι S).obj (Fib.mk ha) ≅ a := + fiberInclusion.mapIso (Functor.objObjPreimageIso (inducedFunctor p S) (Fiber.mk ha)) + +instance Fib.mkIsoSelfIsHomLift {S : 𝒮} {a : 𝒳} (ha : p.obj a = S) : + IsHomLift p (𝟙 S) (Fib.mkIsoSelf ha).hom := + (Functor.objObjPreimageIso (inducedFunctor p S) (Fiber.mk ha)).hom.2 + +section + +variable [IsPreFibered p] {R S : 𝒮} {a : 𝒳} (f : R ⟶ S) (ha : p.obj a = S) + +/-- The domain, taken in `Fib p R`, of some cartesian morphism lifting a given +`f : R ⟶ S` in `𝒮` -/ +noncomputable def mkPullback : Fib p R := + Fib.mk (domain_eq p f (IsPreFibered.pullbackMap ha f)) + +/-- A cartesian morphism lifting `f : R ⟶ S` with domain in the image of `Fib p R` -/ +noncomputable def pullbackMap : (ι R).obj (mkPullback f ha) ⟶ a := + (Fib.mkIsoSelf (domain_eq p f (IsPreFibered.pullbackMap ha f))).hom ≫ + (IsPreFibered.pullbackMap ha f) + +instance pullbackMap.isCartesian : IsCartesian p f (pullbackMap f ha) := by + conv in f => rw [← id_comp f] + simp only [id_comp, pullbackMap] + infer_instance + +end + +section + +variable {R S : 𝒮} {a : 𝒳} {b b' : Fib p R} (f : R ⟶ S) (ψ : (ι R).obj b' ⟶ a) + [IsCartesian p f ψ] (φ : (ι R).obj b ⟶ a) [IsHomLift p f φ] + +/-- Given a fibered category p, b' b in Fib R, and a pullback ψ : b ⟶ a in 𝒳, i.e. +``` +b' b --ψ--> a +| | | +v v v +R ====== R --f--> S +``` +Then the induced map τ : b' ⟶ b can be lifted to the fiber over R -/ +noncomputable def inducedMap : b ⟶ b' := + Fib.homMk (IsCartesian.map p f ψ φ) + +@[reassoc] +lemma inducedMap_comp : (ι R).map (inducedMap f ψ φ) ≫ ψ = φ := by + simp only [inducedMap, Fib.map_homMk, IsCartesian.fac] + +end + +section + +variable [IsFibered p] {R S : 𝒮} {a : 𝒳} {b : Fib p R} + +/-- Given `a : 𝒳`, `b : Fib p R`, and a diagram +``` + b --φ--> a + - - + | | + v v + R --f--> S +``` +It can be factorized as +``` + b --τ--> b'--ψ--> a + - - - + | | | + v v v + R ====== R --f--> S +``` +with `ψ` cartesian over `f` and `τ` a map in `Fib p R`. -/ +lemma fiber_factorization (ha : p.obj a = S) {b : Fib p R} (f : R ⟶ S) (φ : (ι R).obj b ⟶ a) + [IsHomLift p f φ] : ∃ (b' : Fib p R) (τ : b ⟶ b') (ψ : (ι R).obj b' ⟶ a), + IsStronglyCartesian p f ψ ∧ (((ι R).map τ) ≫ ψ = φ) := + let ψ := pullbackMap f ha + ⟨mkPullback f ha, inducedMap f ψ φ, ψ, inferInstance, inducedMap_comp f ψ φ⟩ + +end + +end + +end HasFibers diff --git a/Mathlib/CategoryTheory/FiberedCategory/HomLift.lean b/Mathlib/CategoryTheory/FiberedCategory/HomLift.lean index 5e89f8280f5716..6a98478a89fe4a 100644 --- a/Mathlib/CategoryTheory/FiberedCategory/HomLift.lean +++ b/Mathlib/CategoryTheory/FiberedCategory/HomLift.lean @@ -124,7 +124,7 @@ instance comp {R S T : 𝒮} {a b c : 𝒳} (f : R ⟶ S) (g : S ⟶ T) (φ : a apply CommSq.horiz_comp (commSq p f φ) (commSq p g ψ) /-- If `φ : a ⟶ b` and `ψ : b ⟶ c` lift `𝟙 R`, then so does `φ ≫ ψ` -/ -instance lift_id_comp (R : 𝒮) {a b c : 𝒳} (φ : a ⟶ b) (ψ : b ⟶ c) +instance comp_of_lift_id (R : 𝒮) {a b c : 𝒳} (φ : a ⟶ b) (ψ : b ⟶ c) [p.IsHomLift (𝟙 R) φ] [p.IsHomLift (𝟙 R) ψ] : p.IsHomLift (𝟙 R) (φ ≫ ψ) := comp_id (𝟙 R) ▸ comp p (𝟙 R) (𝟙 R) φ ψ @@ -164,22 +164,37 @@ lemma id_lift_eqToHom_codomain {p : 𝒳 ⥤ 𝒮} {R S : 𝒮} (hRS : R = S) {b p.IsHomLift (eqToHom hRS) (𝟙 b) := by subst hRS hb; simp -instance comp_eqToHom_lift {R S : 𝒮} {a' a b : 𝒳} (f : R ⟶ S) (φ : a ⟶ b) (h : a' = a) - [p.IsHomLift f φ] : p.IsHomLift f (eqToHom h ≫ φ) := by + +section + +variable {R S : 𝒮} {a b : 𝒳} (f : R ⟶ S) (φ : a ⟶ b) [p.IsHomLift f φ] + +instance comp_id_lift : p.IsHomLift f (𝟙 a ≫ φ) := by + simp_all + +instance id_comp_lift : p.IsHomLift f (φ ≫ 𝟙 b) := by + simp_all + +instance lift_id_comp : p.IsHomLift (𝟙 R ≫ f) φ := by + simp_all + +instance lift_comp_id : p.IsHomLift (f ≫ 𝟙 S) φ := by + simp_all + +instance comp_eqToHom_lift {a' : 𝒳} (h : a' = a) : p.IsHomLift f (eqToHom h ≫ φ) := by subst h; simp_all -instance eqToHom_comp_lift {R S : 𝒮} {a b b' : 𝒳} (f : R ⟶ S) (φ : a ⟶ b) (h : b = b') - [p.IsHomLift f φ] : p.IsHomLift f (φ ≫ eqToHom h) := by +instance eqToHom_comp_lift {b' : 𝒳} (h : b = b') : p.IsHomLift f (φ ≫ eqToHom h) := by subst h; simp_all -instance lift_eqToHom_comp {R' R S : 𝒮} {a b : 𝒳} (f : R ⟶ S) (φ : a ⟶ b) (h : R' = R) - [p.IsHomLift f φ] : p.IsHomLift (eqToHom h ≫ f) φ := by +instance lift_eqToHom_comp {R' : 𝒮} (h : R' = R) : p.IsHomLift (eqToHom h ≫ f) φ := by subst h; simp_all -instance lift_comp_eqToHom {R S S' : 𝒮} {a b : 𝒳} (f : R ⟶ S) (φ : a ⟶ b) (h : S = S') - [p.IsHomLift f φ] : p.IsHomLift (f ≫ eqToHom h) φ := by +instance lift_comp_eqToHom {S' : 𝒮} (h : S = S') : p.IsHomLift (f ≫ eqToHom h) φ := by subst h; simp_all +end + @[simp] lemma comp_eqToHom_lift_iff {R S : 𝒮} {a' a b : 𝒳} (f : R ⟶ S) (φ : a ⟶ b) (h : a' = a) : p.IsHomLift f (eqToHom h ≫ φ) ↔ p.IsHomLift f φ where diff --git a/Mathlib/CategoryTheory/Functor/FunctorHom.lean b/Mathlib/CategoryTheory/Functor/FunctorHom.lean index c40fa336208485..f20932e5251104 100644 --- a/Mathlib/CategoryTheory/Functor/FunctorHom.lean +++ b/Mathlib/CategoryTheory/Functor/FunctorHom.lean @@ -149,7 +149,6 @@ def natTransEquiv : (𝟙_ (C ⥤ Type max v' v u) ⟶ F.functorHom G) ≃ (F have := HomObj.congr_app (congr_fun (f.naturality φ) PUnit.unit) Y (𝟙 Y) dsimp [functorHom, homObjFunctor] at this aesop - right_inv _ := rfl end CategoryTheory.Functor diff --git a/Mathlib/CategoryTheory/Functor/TwoSquare.lean b/Mathlib/CategoryTheory/Functor/TwoSquare.lean index ad6fc86ba66f47..0f8ba81ad27904 100644 --- a/Mathlib/CategoryTheory/Functor/TwoSquare.lean +++ b/Mathlib/CategoryTheory/Functor/TwoSquare.lean @@ -60,8 +60,6 @@ the type of natural transformations `T ⋙ R ⟶ L ⋙ B`. -/ def equivNatTrans : TwoSquare T L R B ≃ (T ⋙ R ⟶ L ⋙ B) where toFun := natTrans invFun := mk T L R B - left_inv _ := rfl - right_inv _ := rfl variable {T L R B} diff --git a/Mathlib/CategoryTheory/GradedObject/Trifunctor.lean b/Mathlib/CategoryTheory/GradedObject/Trifunctor.lean index 3be39bd7813f5c..39f258acf61f20 100644 --- a/Mathlib/CategoryTheory/GradedObject/Trifunctor.lean +++ b/Mathlib/CategoryTheory/GradedObject/Trifunctor.lean @@ -326,8 +326,7 @@ noncomputable def isColimitCofan₃MapBifunctor₁₂BifunctorMapObj (j : J) : invFun := fun ⟨⟨i₁, i₂⟩, hi⟩ => ⟨⟨i₁, i₂, i₃⟩, by aesop_cat⟩ left_inv := fun ⟨⟨i₁, i₂, i₃'⟩, hi⟩ => by obtain rfl : i₃ = i₃' := by aesop_cat - rfl - right_inv := fun _ => rfl } + rfl } let c₁₂'' : ∀ (i : ρ₁₂.q ⁻¹' {j}), CofanMapObjFun Z p' (i.1.1, i.1.2) := fun ⟨⟨i₁₂, i₃⟩, hi⟩ => by refine (Cocones.precompose (Iso.hom ?_)).obj ((Cocones.whiskeringEquivalence @@ -505,8 +504,7 @@ noncomputable def isColimitCofan₃MapBifunctorBifunctor₂₃MapObj (j : J) : invFun := fun ⟨⟨i₂, i₃⟩, hi⟩ => ⟨⟨i₁, i₂, i₃⟩, by aesop_cat⟩ left_inv := fun ⟨⟨i₁', i₂, i₃⟩, hi⟩ => by obtain rfl : i₁ = i₁' := by aesop_cat - rfl - right_inv := fun _ => rfl } + rfl } let c₂₃'' : ∀ (i : ρ₂₃.q ⁻¹' {j}), CofanMapObjFun Z p' (i.1.1, i.1.2) := fun ⟨⟨i₁, i₂₃⟩, hi⟩ => by refine (Cocones.precompose (Iso.hom ?_)).obj ((Cocones.whiskeringEquivalence diff --git a/Mathlib/CategoryTheory/Groupoid.lean b/Mathlib/CategoryTheory/Groupoid.lean index 49d19f155db785..6738b9828cdca9 100644 --- a/Mathlib/CategoryTheory/Groupoid.lean +++ b/Mathlib/CategoryTheory/Groupoid.lean @@ -98,8 +98,6 @@ variable (X Y) def Groupoid.isoEquivHom : (X ≅ Y) ≃ (X ⟶ Y) where toFun := Iso.hom invFun f := ⟨f, Groupoid.inv f, (by simp), (by simp)⟩ - left_inv _ := Iso.ext rfl - right_inv _ := rfl variable (C) diff --git a/Mathlib/CategoryTheory/LiftingProperties/Basic.lean b/Mathlib/CategoryTheory/LiftingProperties/Basic.lean index ea36f294050aff..de00801c94e0b3 100644 --- a/Mathlib/CategoryTheory/LiftingProperties/Basic.lean +++ b/Mathlib/CategoryTheory/LiftingProperties/Basic.lean @@ -149,4 +149,23 @@ lemma RetractArrow.rightLiftingProperty ⟨by rw [← Category.assoc, ← sq.w, Category.assoc, RetractArrow.i_w, Category.assoc]⟩ ⟨⟨{ l := sq'.lift ≫ h.r.left}⟩⟩ +namespace Arrow + +/-- Given a morphism `φ : f ⟶ g` in the category `Arrow C`, this is an +abbreviation for the `CommSq.LiftStruct` structure for +the square corresponding to `φ`. -/ +abbrev LiftStruct {f g : Arrow C} (φ : f ⟶ g) := (CommSq.mk φ.w).LiftStruct + +lemma hasLiftingProperty_iff {A B X Y : C} (i : A ⟶ B) (p : X ⟶ Y) : + HasLiftingProperty i p ↔ + ∀ (φ : Arrow.mk i ⟶ Arrow.mk p), Nonempty (LiftStruct φ) := by + constructor + · intro _ φ + have sq : CommSq φ.left i p φ.right := CommSq.mk φ.w + exact ⟨{ l := sq.lift }⟩ + · intro h + exact ⟨fun {f g} sq ↦ ⟨h (Arrow.homMk f g sq.w)⟩⟩ + +end Arrow + end CategoryTheory diff --git a/Mathlib/CategoryTheory/LiftingProperties/ParametrizedAdjunction.lean b/Mathlib/CategoryTheory/LiftingProperties/ParametrizedAdjunction.lean new file mode 100644 index 00000000000000..d4d0cd8d055915 --- /dev/null +++ b/Mathlib/CategoryTheory/LiftingProperties/ParametrizedAdjunction.lean @@ -0,0 +1,289 @@ +/- +Copyright (c) 2025 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +import Mathlib.CategoryTheory.LiftingProperties.Basic +import Mathlib.CategoryTheory.Limits.Shapes.Pullback.CommSq +import Mathlib.CategoryTheory.Adjunction.Parametrized + +/-! +# Lifting properties and parametrized adjunctions + +Let `F : C₁ ⥤ C₂ ⥤ C₃`. Given morphisms `f₁ : X₁ ⟶ Y₁` in `C₁` +and `f₂ : X₂ ⟶ Y₂` in `C₂`, we introduce a structure +`F.PushoutObjObj f₁ f₂` which contains the data of a +pushout of `(F.obj Y₁).obj X₂` and `(F.obj X₁).obj Y₂` +along `(F.obj X₁).obj X₂`. If `sq₁₂ : F.PushoutObjObj f₁ f₂`, +we have a canonical "inclusion" `sq₁₂.ι : sq₁₂.pt ⟶ (F.obj Y₁).obj Y₂`. + +Similarly, if we have a bifunctor `G : C₁ᵒᵖ ⥤ C₃ ⥤ C₂`, and +morphisms `f₁ : X₁ ⟶ Y₁` in `C₁` and `f₃ : X₃ ⟶ Y₃` in `C₃`, +we introduce a structure `F.PullbackObjObj f₁ f₃` which +contains the data of a pullback of `(G.obj (op X₁)).obj X₃` +and `(G.obj (op Y₁)).obj Y₃` over `(G.obj (op X₁)).obj Y₃`. +If `sq₁₃ : F.PullbackObjObj f₁ f₃`, we have a canonical +projection `sq₁₃.π : (G.obj Y₁).obj X₃ ⟶ sq₁₃.pt`. + +Now, if we have a parametrized adjunction `adj₂ : F ⊣₂ G`, +`sq₁₂ : F.PushoutObjObj f₁ f₂` and `sq₁₃ : G.PullbackObjObj f₁ f₃`, +we show that `sq₁₂.ι` has the left lifting property with respect to +`f₃` if and only if `f₂` has the left lifting property with respect +to `sq₁₃.π`: this is the lemma `ParametrizedAdjunction.hasLiftingProperty_iff`. + +-/ + +universe v₁ v₂ v₃ u₁ u₂ u₃ + +namespace CategoryTheory + +open Opposite Limits + +variable {C₁ : Type u₁} {C₂ : Type u₂} {C₃ : Type u₃} + [Category.{v₁} C₁] [Category.{v₂} C₂] [Category.{v₃} C₃] + (F : C₁ ⥤ C₂ ⥤ C₃) (G : C₁ᵒᵖ ⥤ C₃ ⥤ C₂) + +namespace Functor + +section + +variable {X₁ Y₁ : C₁} (f₁ : X₁ ⟶ Y₁) {X₂ Y₂ : C₂} (f₂ : X₂ ⟶ Y₂) + +/-- Given a bifunctor `F : C₁ ⥤ C₂ ⥤ C₃`, and morphisms `f₁ : X₁ ⟶ Y₁` in `C₁` +and `f₂ : X₂ ⟶ Y₂` in `C₂`, this structure contains the data of +a pushout of `(F.obj Y₁).obj X₂` and `(F.obj X₁).obj Y₂` +along `(F.obj X₁).obj X₂`. -/ +structure PushoutObjObj where + /-- the pushout -/ + pt : C₃ + /-- the first inclusion -/ + inl : (F.obj Y₁).obj X₂ ⟶ pt + /-- the second inclusion -/ + inr : (F.obj X₁).obj Y₂ ⟶ pt + isPushout : IsPushout ((F.map f₁).app X₂) ((F.obj X₁).map f₂) inl inr + +namespace PushoutObjObj + +/-- The `PushoutObjObj` structure given by the pushout of the colimits API. -/ +@[simps] +noncomputable def ofHasPushout + [HasPushout ((F.map f₁).app X₂) ((F.obj X₁).map f₂)] : + F.PushoutObjObj f₁ f₂ := + { isPushout := IsPushout.of_hasPushout _ _, .. } + +variable {F f₁ f₂} (sq : F.PushoutObjObj f₁ f₂) + +/-- The "inclusion" `sq.pt ⟶ (F.obj Y₁).obj Y₂` when +`sq : F.PushoutObjObj f₁ f₂`. -/ +noncomputable def ι : sq.pt ⟶ (F.obj Y₁).obj Y₂ := + sq.isPushout.desc ((F.obj Y₁).map f₂) ((F.map f₁).app Y₂) (by simp) + +@[reassoc (attr := simp)] +lemma inl_ι : sq.inl ≫ sq.ι = (F.obj Y₁).map f₂ := by simp [ι] + +@[reassoc (attr := simp)] +lemma inr_ι : sq.inr ≫ sq.ι = (F.map f₁).app Y₂ := by simp [ι] + +/-- Given `sq : F.PushoutObjObj f₁ f₂`, flipping the pushout square gives +`sq.flip : F.flip.PushoutObjObj f₂ f₁`. -/ +@[simps] +def flip : F.flip.PushoutObjObj f₂ f₁ where + pt := sq.pt + inl := sq.inr + inr := sq.inl + isPushout := sq.isPushout.flip + +@[simp] +lemma ι_flip : sq.flip.ι = sq.ι := by + apply sq.flip.isPushout.hom_ext + · rw [inl_ι, flip_inl, inr_ι, flip_obj_map] + · rw [inr_ι, flip_inr, inl_ι, flip_map_app] + +end PushoutObjObj + +end + +section + +variable {X₁ Y₁ : C₁} (f₁ : X₁ ⟶ Y₁) {X₃ Y₃ : C₃} (f₃ : X₃ ⟶ Y₃) + +/-- Given a bifunctor `G : C₁ᵒᵖ ⥤ C₃ ⥤ C₂`, and morphisms `f₁ : X₁ ⟶ Y₁` in `C₁` +and `f₃ : X₃ ⟶ Y₃` in `C₃`, this structure contains the data of +a pullback of `(G.obj (op X₁)).obj X₃` +and `(G.obj (op Y₁)).obj Y₃` over `(G.obj (op X₁)).obj Y₃`. -/ +structure PullbackObjObj where + /-- the pullback -/ + pt : C₂ + /-- the first projection -/ + fst : pt ⟶ (G.obj (op X₁)).obj X₃ + /-- the second projection -/ + snd : pt ⟶ (G.obj (op Y₁)).obj Y₃ + isPullback : IsPullback fst snd ((G.obj (op X₁)).map f₃) + ((G.map f₁.op).app Y₃) + +namespace PullbackObjObj + +/-- The `PullbackObjObj` structure given by the pullback of the limits API. -/ +@[simps] +noncomputable def ofHasPullback + [HasPullback ((G.obj (op X₁)).map f₃) ((G.map f₁.op).app Y₃)] : + G.PullbackObjObj f₁ f₃ := + { isPullback := IsPullback.of_hasPullback _ _, ..} + +variable {G f₁ f₃} (sq : G.PullbackObjObj f₁ f₃) + +/-- The projection `(G.obj (op Y₁)).obj X₃ ⟶ sq.pt` when +`sq : G.PullbackObjObj f₁ f₃`. -/ +noncomputable def π : (G.obj (op Y₁)).obj X₃ ⟶ sq.pt := + sq.isPullback.lift ((G.map f₁.op).app X₃) ((G.obj (op Y₁)).map f₃) (by simp) + +@[reassoc (attr := simp)] +lemma π_fst : sq.π ≫ sq.fst = (G.map f₁.op).app X₃ := by simp [π] + +@[reassoc (attr := simp)] +lemma π_snd : sq.π ≫ sq.snd = (G.obj (op Y₁)).map f₃ := by simp [π] + +end PullbackObjObj + +end + +end Functor + +namespace ParametrizedAdjunction + +variable {F G} (adj₂ : F ⊣₂ G) + {X₁ Y₁ : C₁} {f₁ : X₁ ⟶ Y₁} {X₂ Y₂ : C₂} {f₂ : X₂ ⟶ Y₂} + {X₃ Y₃ : C₃} {f₃ : X₃ ⟶ Y₃} + (sq₁₂ : F.PushoutObjObj f₁ f₂) (sq₁₃ : G.PullbackObjObj f₁ f₃) + +/-- Given a parametrized adjunction `F ⊣₂ G` between bifunctors, and structures +`sq₁₂ : F.PushoutObjObj f₁ f₂` and `sq₁₃ : G.PullbackObjObj f₁ f₃`, there are +as many commutative squares with left map `sq₁₂.ι` and right map `f₃` +as commutative squares with left map `f₂` and right map `sq₁₃.π`. -/ +@[simps! apply_left symm_apply_right] +noncomputable def arrowHomEquiv : + (Arrow.mk sq₁₂.ι ⟶ Arrow.mk f₃) ≃ + (Arrow.mk f₂ ⟶ Arrow.mk sq₁₃.π) where + toFun α := + Arrow.homMk (adj₂.homEquiv (sq₁₂.inl ≫ α.left)) + (sq₁₃.isPullback.lift + (adj₂.homEquiv (sq₁₂.inr ≫ α.left)) (adj₂.homEquiv α.right) + (by simp [← adj₂.homEquiv_naturality_one, + ← adj₂.homEquiv_naturality_three])) (by + apply sq₁₃.isPullback.hom_ext + · simp [← adj₂.homEquiv_naturality_two, + ← adj₂.homEquiv_naturality_one, + sq₁₂.isPushout.w_assoc] + · simp [← adj₂.homEquiv_naturality_two, + ← adj₂.homEquiv_naturality_three]) + invFun β := + Arrow.homMk + (sq₁₂.isPushout.desc + (adj₂.homEquiv.symm β.left) + (adj₂.homEquiv.symm (β.right ≫ sq₁₃.fst)) (by + have := Arrow.w β =≫ sq₁₃.fst + dsimp at this + simp only [Category.assoc, sq₁₃.π_fst] at this + simp only [← adj₂.homEquiv_symm_naturality_one, + ← adj₂.homEquiv_symm_naturality_two, + Arrow.mk_left, Arrow.mk_right, this])) + (adj₂.homEquiv.symm (β.right ≫ sq₁₃.snd)) (by + apply sq₁₂.isPushout.hom_ext + · have := Arrow.w β =≫ sq₁₃.snd + dsimp at this + simp only [Category.assoc, sq₁₃.π_snd] at this + simp [← adj₂.homEquiv_symm_naturality_two, + ← adj₂.homEquiv_symm_naturality_three, this] + · simp [← adj₂.homEquiv_symm_naturality_one, + ← adj₂.homEquiv_symm_naturality_three, sq₁₃.isPullback.w]) + left_inv α := by + ext + · apply sq₁₂.isPushout.hom_ext <;> simp + · simp + right_inv β := by + ext + · simp + · apply sq₁₃.isPullback.hom_ext <;> simp + +@[reassoc (attr := simp)] +lemma arrowHomEquiv_apply_right_fst (α : Arrow.mk sq₁₂.ι ⟶ Arrow.mk f₃) : + ((adj₂.arrowHomEquiv sq₁₂ sq₁₃) α).right ≫ sq₁₃.fst = adj₂.homEquiv (sq₁₂.inr ≫ α.left) := + IsPullback.lift_fst _ _ _ _ + +@[reassoc (attr := simp)] +lemma arrowHomEquiv_apply_right_snd (α : Arrow.mk sq₁₂.ι ⟶ Arrow.mk f₃) : + ((adj₂.arrowHomEquiv sq₁₂ sq₁₃) α).right ≫ sq₁₃.snd = adj₂.homEquiv α.right := + IsPullback.lift_snd _ _ _ _ + +@[reassoc (attr := simp)] +lemma inl_arrowHomEquiv_symm_apply_left (β : Arrow.mk f₂ ⟶ Arrow.mk sq₁₃.π) : + sq₁₂.inl ≫ ((adj₂.arrowHomEquiv sq₁₂ sq₁₃).symm β).left = adj₂.homEquiv.symm β.left := + IsPushout.inl_desc _ _ _ _ + +@[reassoc (attr := simp)] +lemma inr_arrowHomEquiv_symm_apply_left (β : Arrow.mk f₂ ⟶ Arrow.mk sq₁₃.π) : + sq₁₂.inr ≫ ((adj₂.arrowHomEquiv sq₁₂ sq₁₃).symm β).left = + adj₂.homEquiv.symm (β.right ≫ sq₁₃.fst) := + IsPushout.inr_desc _ _ _ _ + +/-- Given a parametrized adjunction `F ⊣₂ G` between bifunctors, structures +`sq₁₂ : F.PushoutObjObj f₁ f₂` and `sq₁₃ : G.PullbackObjObj f₁ f₃`, +there are as many liftings for the commutative square given by a +map `α : Arrow.mk sq₁₂.ι ⟶ Arrow.mk f₃` as there are liftings +for the square given by the corresponding map `Arrow.mk f₂ ⟶ Arrow.mk sq₁₃.π`. -/ +noncomputable def liftStructEquiv (α : Arrow.mk sq₁₂.ι ⟶ Arrow.mk f₃) : + Arrow.LiftStruct α ≃ Arrow.LiftStruct (adj₂.arrowHomEquiv sq₁₂ sq₁₃ α) where + toFun l := + { l := adj₂.homEquiv l.l + fac_left := by + have := l.fac_left + dsimp at this ⊢ + simp only [← adj₂.homEquiv_naturality_two, ← this, + sq₁₂.inl_ι_assoc] + fac_right := by + apply sq₁₃.isPullback.hom_ext + · have := l.fac_left + dsimp at this ⊢ + simp only [Category.assoc, sq₁₃.π_fst, ← adj₂.homEquiv_naturality_one, + arrowHomEquiv_apply_right_fst, Arrow.mk_left, ← this, sq₁₂.inr_ι_assoc] + · have := l.fac_right + dsimp at this ⊢ + simp only [Category.assoc, sq₁₃.π_snd, ← this, adj₂.homEquiv_naturality_three, + arrowHomEquiv_apply_right_snd, Arrow.mk_right] } + invFun l := + { l := adj₂.homEquiv.symm l.l + fac_left := by + apply sq₁₂.isPushout.hom_ext + · have := l.fac_left + dsimp at this ⊢ + simp only [sq₁₂.inl_ι_assoc, ← adj₂.homEquiv_symm_naturality_two, + this, Equiv.symm_apply_apply] + · have := l.fac_right =≫ sq₁₃.fst + dsimp at this ⊢ + simp only [Category.assoc, sq₁₃.π_fst, IsPullback.lift_fst] at this + simp only [sq₁₂.inr_ι_assoc, ← adj₂.homEquiv_symm_naturality_one, + this, Equiv.symm_apply_apply, arrowHomEquiv_apply_right_fst, Arrow.mk_left] + fac_right := by + have := l.fac_right =≫ sq₁₃.snd + dsimp at this ⊢ + simp only [Category.assoc, sq₁₃.π_snd, arrowHomEquiv_apply_right_snd, + Arrow.mk_right] at this + rw [← adj₂.homEquiv_symm_naturality_three, this, + Equiv.symm_apply_apply] } + left_inv _ := by aesop + right_inv _ := by aesop + +include adj₂ in +lemma hasLiftingProperty_iff : + HasLiftingProperty sq₁₂.ι f₃ ↔ HasLiftingProperty f₂ sq₁₃.π := by + simp only [Arrow.hasLiftingProperty_iff] + constructor + · intro h β + obtain ⟨α, rfl⟩ := (adj₂.arrowHomEquiv sq₁₂ sq₁₃).surjective β + exact ⟨adj₂.liftStructEquiv sq₁₂ sq₁₃ α (h α).some⟩ + · intro h α + exact ⟨(adj₂.liftStructEquiv sq₁₂ sq₁₃ α).symm (h _).some⟩ + +end ParametrizedAdjunction + +end CategoryTheory diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean b/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean new file mode 100644 index 00000000000000..426534031faa86 --- /dev/null +++ b/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean @@ -0,0 +1,275 @@ +/- +Copyright (c) 2025 Robin Carlier. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robin Carlier, Joël Riou +-/ +import Mathlib.CategoryTheory.Functor.Currying +import Mathlib.CategoryTheory.Limits.HasLimits + +/-! +# Preservations of limits for bifunctors + +Let `G : C₁ ⥤ C₂ ⥤ C` a functor. We introduce a class `PreservesLimit₂ K₁ K₂ G` that encodes +the hypothesis that the curried functor `F : C₁ × C₂ ⥤ C` preserves limits of the diagram +`K₁ × K₂ : J₁ × J₂ ⥤ C₁ × C₂`. We give a basic API to extract isomorphisms +$\lim_{(j_1,j_2)} G(K_1(j_1), K_2(j_2)) \simeq G(\lim K_1, \lim K_2)$ +out of this typeclass. + +-/ + +namespace CategoryTheory + +open Category Limits + +variable {J₁ J₂ : Type*} [Category J₁] [Category J₂] + {C₁ C₂ C : Type*} [Category C₁] [Category C₂] [Category C] + +/-- Given a bifunctor `G : C₁ ⥤ C₂ ⥤ C`, diagrams `K₁ : J₁ ⥤ C₁` and `K₂ : J₂ ⥤ C₂`, and cocones +over these diagrams, `G.mapCocone₂ c₁ c₂` is the cocone over the diagram `J₁ × J₂ ⥤ C` obtained +by applying `G` to both `c₁` and `c₂`. -/ +@[simps!] +def Functor.mapCocone₂ (G : C₁ ⥤ C₂ ⥤ C) {K₁ : J₁ ⥤ C₁} {K₂ : J₂ ⥤ C₂} + (c₁ : Cocone K₁) (c₂ : Cocone K₂) : + Cocone <| uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G) where + pt := (G.obj c₁.pt).obj c₂.pt + ι := + { app := fun ⟨j₁, j₂⟩ ↦ (G.map <| c₁.ι.app j₁).app _ ≫ (G.obj _).map (c₂.ι.app j₂) + naturality := by + rintro ⟨j₁, j₂⟩ ⟨k₁, k₂⟩ ⟨f₁, f₂⟩ + dsimp + simp only [assoc, comp_id, NatTrans.naturality_assoc, + ← Functor.map_comp, NatTrans.naturality, const_obj_map, const_obj_obj, + ← NatTrans.comp_app_assoc, c₁.w] } + +/-- Given a bifunctor `G : C₁ ⥤ C₂ ⥤ C`, diagrams `K₁ : J₁ ⥤ C₁` and `K₂ : J₂ ⥤ C₂`, and cones +over these diagrams, `G.mapCone₂ c₁ c₂` is the cone over the diagram `J₁ × J₂ ⥤ C` obtained +by applying `G` to both `c₁` and `c₂`. -/ +@[simps!] +def Functor.mapCone₂ (G : C₁ ⥤ C₂ ⥤ C) {K₁ : J₁ ⥤ C₁} {K₂ : J₂ ⥤ C₂} + (c₁ : Cone K₁) (c₂ : Cone K₂) : + Cone <| uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G) where + pt := (G.obj c₁.pt).obj c₂.pt + π := + { app := fun ⟨j₁, j₂⟩ ↦ (G.map <| c₁.π.app j₁).app _ ≫ (G.obj _).map (c₂.π.app j₂) + naturality := by + rintro ⟨j₁, j₂⟩ ⟨k₁, k₂⟩ ⟨f₁, f₂⟩ + dsimp + simp only [assoc, id_comp, NatTrans.naturality_assoc, + ← Functor.map_comp, NatTrans.naturality, const_obj_map, const_obj_obj, + ← NatTrans.comp_app_assoc, c₁.w, c₂.w] } + +namespace Limits + +/-- A functor `PreservesColimit₂ K₁ K₂` if whenever `c₁` is a colimit cocone and `c₂` is a colimit +cocone then `G.mapCocone₂ c₁ c₂` is a colimit cocone. This can be thought of as the data of an +isomorphism +$\mathrm{colim}_{(j_1,j_2)} G(K_1(j_1),K_2(j_2)) \simeq G(\mathrm{colim} K_1,\mathrm{colim} K_2)$. +-/ +class PreservesColimit₂ (K₁ : J₁ ⥤ C₁) (K₂ : J₂ ⥤ C₂) (G : C₁ ⥤ C₂ ⥤ C) : Prop where + nonempty_isColimit_mapCocone₂ {c₁ : Cocone K₁} (hc₁ : IsColimit c₁) + {c₂ : Cocone K₂} (hc₂ : IsColimit c₂) : + Nonempty <| IsColimit <| G.mapCocone₂ c₁ c₂ + +/-- A functor `PreservesLimit₂ K₁ K₂` if whenever `c₁` is a limit cone and `c₂` is a limit +cone then `G.mapCone₂ c₁ c₂` is a limit cone. This can be thought of as the data of an +isomorphism $\lim_{(j_1,j_2)} G(K_1(j_1), K_2(j_2)) \simeq G(\lim K_1, \lim K_2)$. +-/ +class PreservesLimit₂ (K₁ : J₁ ⥤ C₁) (K₂ : J₂ ⥤ C₂) (G : C₁ ⥤ C₂ ⥤ C) : Prop where + nonempty_isLimit_mapCone₂ {c₁ : Cone K₁} (hc₁ : IsLimit c₁) + {c₂ : Cone K₂} (hc₂ : IsLimit c₂) : + Nonempty <| IsLimit <| G.mapCone₂ c₁ c₂ + +variable {K₁ : J₁ ⥤ C₁} {K₂ : J₂ ⥤ C₂} (G : C₁ ⥤ C₂ ⥤ C) + +/-- If `PreservesColimit₂ K₁ K₂ G`, obtain that `G.mapCocone₂ c₁ c₂` is a colimit cocone +whenever c₁ c₂ are colimit cocones. -/ +noncomputable def isColimitOfPreserves₂ [PreservesColimit₂ K₁ K₂ G] + {c₁ : Cocone K₁} (hc₁ : IsColimit c₁) + {c₂ : Cocone K₂} (hc₂ : IsColimit c₂) : + IsColimit (G.mapCocone₂ c₁ c₂) := + PreservesColimit₂.nonempty_isColimit_mapCocone₂ hc₁ hc₂|>.some + +/-- If `PreservesLimit₂ K₁ K₂ G`, obtain that `G.mapCone₂ c₁ c₂` is a limit cone +whenever c₁ c₂ are limit cones. -/ +noncomputable def isLimitOfPreserves₂ [PreservesLimit₂ K₁ K₂ G] + {c₁ : Cone K₁} (hc₁ : IsLimit c₁) + {c₂ : Cone K₂} (hc₂ : IsLimit c₂) : + IsLimit (G.mapCone₂ c₁ c₂) := + PreservesLimit₂.nonempty_isLimit_mapCone₂ hc₁ hc₂|>.some + +instance [HasColimit K₁] [HasColimit K₂] [PreservesColimit₂ K₁ K₂ G] : + HasColimit <| uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G) where + exists_colimit := ⟨{ + cocone := _ + isColimit := + PreservesColimit₂.nonempty_isColimit_mapCocone₂ + (getColimitCocone K₁).isColimit + (getColimitCocone K₂).isColimit|>.some }⟩ + +instance [HasLimit K₁] [HasLimit K₂] [PreservesLimit₂ K₁ K₂ G] : + HasLimit <| uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G) where + exists_limit := ⟨{ + cone := _ + isLimit := + PreservesLimit₂.nonempty_isLimit_mapCone₂ + (getLimitCone K₁).isLimit + (getLimitCone K₂).isLimit|>.some }⟩ + +namespace PreservesColimit₂ + +variable [PreservesColimit₂ K₁ K₂ G] + +/-- Given a `PreservesColimit₂` instance, extract the isomorphism between +a colimit of `uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)` and +`(G.obj c₁).obj c₂` where c₁ (resp. c₂) is a colimit of `K₁` (resp `K₂`). -/ +noncomputable def isoObjCoconePointsOfIsColimit + {c₁ : Cocone K₁} (hc₁ : IsColimit c₁) + {c₂ : Cocone K₂} (hc₂ : IsColimit c₂) + {c₃ : Cocone <| uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)} + (hc₃ : IsColimit c₃) : + (G.obj c₁.pt).obj c₂.pt ≅ c₃.pt := + IsColimit.coconePointUniqueUpToIso (isColimitOfPreserves₂ G hc₁ hc₂) hc₃ + +section + +variable {c₁ : Cocone K₁} (hc₁ : IsColimit c₁) + {c₂ : Cocone K₂} (hc₂ : IsColimit c₂) + {c₃ : Cocone <| uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)} + (hc₃ : IsColimit c₃) + +/-- Characterize the inverse direction of the isomorphism +`PreservesColimit₂.isoObjCoconePointsOfIsColimit` w.r.t the canonical maps to the colimit. -/ +@[reassoc (attr := simp)] +lemma ι_comp_isoObjConePointsOfIsColimit_inv (j : J₁ × J₂) : + c₃.ι.app j ≫ + (isoObjCoconePointsOfIsColimit G hc₁ hc₂ hc₃).inv = + (G.map <| c₁.ι.app j.1).app (K₂.obj j.2) ≫ (G.obj c₁.pt).map (c₂.ι.app j.2) := by + dsimp [isoObjCoconePointsOfIsColimit, Functor.mapCocone₂] + aesop_cat + +/-- Characterize the forward direction of the isomorphism +`PreservesColimit₂.isoObjCoconePointsOfIsColimit` w.r.t the canonical maps to the colimit. -/ +@[reassoc (attr := simp)] +lemma map_ι_comp_isoObjConePointsOfIsColimit_hom (j : J₁ × J₂) : + (G.map (c₁.ι.app j.1)).app (K₂.obj j.2) ≫ (G.obj c₁.pt).map (c₂.ι.app j.2) ≫ + (isoObjCoconePointsOfIsColimit G hc₁ hc₂ hc₃).hom = + c₃.ι.app j := by + rw [← Category.assoc, ← Iso.eq_comp_inv] + simp + +end + +variable (K₁ K₂) [HasColimit K₁] [HasColimit K₂] + +/-- Extract the isomorphism between +`colim (uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G))` and +`(G.obj (colim K₁)).obj (colim K₂)` from a `PreservesColimit₂` instance, provided the relevant +colimits exist. -/ +noncomputable def isoColimitUncurryWhiskeringLeft₂ : + colimit (uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)) ≅ + (G.obj <| colimit K₁).obj (colimit K₂) := + isoObjCoconePointsOfIsColimit G + (colimit.isColimit _) (colimit.isColimit _) (colimit.isColimit _)|>.symm + +/-- Characterize the forward direction of the isomorphism +`PreservesColimit₂.isoColimitUncurryWhiskeringLeft₂` w.r.t the canonical maps to the colimit. -/ +@[reassoc (attr := simp)] +lemma ι_comp_isoColimitUncurryWhiskeringLeft₂_hom (j : J₁ × J₂) : + colimit.ι (uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)) j ≫ + (PreservesColimit₂.isoColimitUncurryWhiskeringLeft₂ K₁ K₂ G).hom = + (G.map <| colimit.ι K₁ j.1).app (K₂.obj j.2) ≫ (G.obj <| colimit K₁).map (colimit.ι K₂ j.2) := + ι_comp_isoObjConePointsOfIsColimit_inv G + (colimit.isColimit _) (colimit.isColimit _) (colimit.isColimit _) j + +/-- Characterize the forward direction of the isomorphism +`PreservesColimit₂.isoColimitUncurryWhiskeringLeft₂` w.r.t the canonical maps to the colimit. -/ +@[reassoc (attr := simp)] +lemma map_ι_comp_isoColimitUncurryWhiskeringLeft₂_inv (j : J₁ × J₂) : + (G.map (colimit.ι K₁ j.1)).app (K₂.obj j.2) ≫ (G.obj <| colimit K₁).map (colimit.ι K₂ j.2) ≫ + (PreservesColimit₂.isoColimitUncurryWhiskeringLeft₂ K₁ K₂ G).inv = + colimit.ι (uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)) j := + map_ι_comp_isoObjConePointsOfIsColimit_hom G + (colimit.isColimit _) (colimit.isColimit _) (colimit.isColimit _) j + +end PreservesColimit₂ + +namespace PreservesLimit₂ + +variable [PreservesLimit₂ K₁ K₂ G] + +/-- Given a `PreservesLimit₂` instance, extract the isomorphism between +a limit of `uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)` and +`(G.obj c₁).obj c₂` where c₁ (resp. c₂) is a limit of `K₁` (resp `K₂`). -/ +noncomputable def isoObjConePointsOfIsLimit + {c₁ : Cone K₁} (hc₁ : IsLimit c₁) + {c₂ : Cone K₂} (hc₂ : IsLimit c₂) + {c₃ : Cone <| uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)} + (hc₃ : IsLimit c₃) : + (G.obj c₁.pt).obj c₂.pt ≅ c₃.pt := + IsLimit.conePointUniqueUpToIso (isLimitOfPreserves₂ G hc₁ hc₂) hc₃ + +section + +variable {c₁ : Cone K₁} (hc₁ : IsLimit c₁) + {c₂ : Cone K₂} (hc₂ : IsLimit c₂) + {c₃ : Cone <| uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)} + (hc₃ : IsLimit c₃) + +/-- Characterize the forward direction of the isomorphism +`PreservesLimit₂.isoObjConePointsOfIsLimit` w.r.t the canonical maps to the limit. -/ +@[reassoc (attr := simp)] +lemma isoObjConePointsOfIsLimit_hom_comp_π (j : J₁ × J₂) : + (isoObjConePointsOfIsLimit G hc₁ hc₂ hc₃).hom ≫ c₃.π.app j = + (G.map <| c₁.π.app j.1).app c₂.pt ≫ (G.obj <| K₁.obj j.1).map (c₂.π.app j.2) := by + dsimp [isoObjConePointsOfIsLimit, Functor.mapCocone₂] + aesop_cat + +/-- Characterize the inverse direction of the isomorphism +`PreservesLimit₂.isoObjConePointsOfIsLimit` w.r.t the canonical maps to the limit. -/ +@[reassoc (attr := simp)] +lemma isoObjConePointsOfIsColimit_inv_comp_map_π (j : J₁ × J₂) : + (isoObjConePointsOfIsLimit G hc₁ hc₂ hc₃).inv ≫ + (G.map (c₁.π.app j.1)).app c₂.pt ≫ (G.obj <| K₁.obj j.1).map (c₂.π.app j.2) = + c₃.π.app j := by + rw [Iso.inv_comp_eq] + simp + +end + +variable (K₁) (K₂) [HasLimit K₁] [HasLimit K₂] + +/-- Extract the isomorphism between +`colim (uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G))` and +`(G.obj (colim K₁)).obj (colim K₂)` from a `PreservesLimit₂` instance, provided the relevant +limits exist. -/ +noncomputable def isoLimitUncurryWhiskeringLeft₂ : + limit (uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)) ≅ + (G.obj <| limit K₁).obj (limit K₂) := + isoObjConePointsOfIsLimit G + (limit.isLimit _) (limit.isLimit _) (limit.isLimit _)|>.symm + +/-- Characterize the inverse direction of the isomorphism +`PreservesLimit₂.isoLimitUncurryWhiskeringLeft₂` w.r.t the canonical maps to the limit. -/ +@[reassoc (attr := simp)] +lemma isoLimitUncurryWhiskeringLeft₂_inv_comp_π (j : J₁ × J₂) : + (PreservesLimit₂.isoLimitUncurryWhiskeringLeft₂ K₁ K₂ G).inv ≫ + limit.π (uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)) j = + (G.map <| limit.π K₁ j.1).app (limit K₂) ≫ (G.obj <| K₁.obj j.1).map (limit.π K₂ j.2) := + isoObjConePointsOfIsLimit_hom_comp_π G + (limit.isLimit _) (limit.isLimit _) (limit.isLimit _) _ + +/-- Characterize the forward direction of the isomorphism +`PreservesLimit₂.isoLimitUncurryWhiskeringLeft₂` w.r.t the canonical maps to the limit. -/ +@[reassoc (attr := simp)] +lemma isoLimitUncurryWhiskeringLeft₂_hom_comp_map_π (j : J₁ × J₂) : + (PreservesLimit₂.isoLimitUncurryWhiskeringLeft₂ K₁ K₂ G).hom ≫ + (G.map (limit.π K₁ j.1)).app (limit K₂) ≫ (G.obj <| K₁.obj j.1).map (limit.π K₂ j.2) = + limit.π (uncurry.obj (whiskeringLeft₂ C|>.obj K₁|>.obj K₂|>.obj G)) j := + isoObjConePointsOfIsColimit_inv_comp_map_π G + (limit.isLimit _) (limit.isLimit _) (limit.isLimit _) _ + +end PreservesLimit₂ + +end Limits + +end CategoryTheory diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Shapes/AbelianImages.lean b/Mathlib/CategoryTheory/Limits/Preserves/Shapes/AbelianImages.lean index 9d3fe13354ee4c..867eb7e0797133 100644 --- a/Mathlib/CategoryTheory/Limits/Preserves/Shapes/AbelianImages.lean +++ b/Mathlib/CategoryTheory/Limits/Preserves/Shapes/AbelianImages.lean @@ -21,12 +21,17 @@ open CategoryTheory Limits namespace CategoryTheory.Abelian -variable {C : Type u₁} [Category.{v₁} C] [HasZeroMorphisms C] [HasKernels C] [HasCokernels C] -variable {D : Type u₂} [Category.{v₂} D] [HasZeroMorphisms D] [HasKernels D] [HasCokernels D] -variable (F : C ⥤ D) [F.PreservesZeroMorphisms] [PreservesLimitsOfShape WalkingParallelPair F] - [PreservesColimitsOfShape WalkingParallelPair F] +variable {C : Type u₁} [Category.{v₁} C] [HasZeroMorphisms C] +variable {D : Type u₂} [Category.{v₂} D] [HasZeroMorphisms D] +variable (F : C ⥤ D) [F.PreservesZeroMorphisms] variable {X Y : C} (f : X ⟶ Y) +section Images + +variable [HasCokernel f] [HasKernel (cokernel.π f)] [PreservesColimit (parallelPair f 0) F] + [PreservesLimit (parallelPair (cokernel.π f) 0) F] [HasCokernel (F.map f)] + [HasKernel (cokernel.π (F.map f))] + /-- If a functor preserves kernels and cokernels, it preserves abelian images. -/ def PreservesImage.iso : F.obj (Abelian.image f) ≅ Abelian.image (F.map f) := PreservesKernel.iso F _ ≪≫ kernel.mapIso _ _ (Iso.refl _) (PreservesCokernel.iso F _) (by simp) @@ -53,6 +58,14 @@ theorem PreservesImage.factorThruImage_iso_inv : F.map (Abelian.factorThruImage f) := by simp [Iso.comp_inv_eq] +end Images + +section Coimages + +variable [HasKernel f] [HasCokernel (kernel.ι f)] [PreservesLimit (parallelPair f 0) F] + [PreservesColimit (parallelPair (kernel.ι f) 0) F] [HasKernel (F.map f)] + [HasCokernel (kernel.ι (F.map f))] + /-- If a functor preserves kernels and cokernels, it preserves abelian coimages. -/ def PreservesCoimage.iso : F.obj (Abelian.coimage f) ≅ Abelian.coimage (F.map f) := PreservesCokernel.iso F _ ≪≫ cokernel.mapIso _ _ (PreservesKernel.iso F _) (Iso.refl _) (by simp) @@ -79,6 +92,14 @@ theorem PreservesCoimage.iso_inv_π : Abelian.coimage.π (F.map f) ≫ (PreservesCoimage.iso F f).inv = F.map (Abelian.coimage.π f) := by simp [Iso.comp_inv_eq] +end Coimages + +variable [HasKernel f] [HasCokernel f] [HasKernel (cokernel.π f)] [HasCokernel (kernel.ι f)] + [PreservesLimit (parallelPair f 0) F] [PreservesColimit (parallelPair f 0) F] + [PreservesLimit (parallelPair (cokernel.π f) 0) F] + [PreservesColimit (parallelPair (kernel.ι f) 0) F] + [HasKernel (cokernel.π (F.map f))] [HasCokernel (kernel.ι (F.map f))] + theorem PreservesCoimage.hom_coimageImageComparison : (PreservesCoimage.iso F f).hom ≫ coimageImageComparison (F.map f) = F.map (coimageImageComparison f) ≫ (PreservesImage.iso F f).hom := by diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Ulift.lean b/Mathlib/CategoryTheory/Limits/Preserves/Ulift.lean index 659b15231ce76e..2cddc6c1ed464d 100644 --- a/Mathlib/CategoryTheory/Limits/Preserves/Ulift.lean +++ b/Mathlib/CategoryTheory/Limits/Preserves/Ulift.lean @@ -33,8 +33,6 @@ def sectionsEquiv {J : Type*} [Category J] (K : J ⥤ Type u) : K.sections ≃ (K ⋙ uliftFunctor.{v, u}).sections where toFun := fun ⟨u, hu⟩ => ⟨fun j => ⟨u j⟩, fun f => by simp [hu f]⟩ invFun := fun ⟨u, hu⟩ => ⟨fun j => (u j).down, @fun j j' f => by simp [← hu f]⟩ - left_inv _ := rfl - right_inv _ := rfl /-- The functor `uliftFunctor : Type u ⥤ Type (max u v)` preserves limits of arbitrary size. diff --git a/Mathlib/CategoryTheory/Limits/Shapes/SingleObj.lean b/Mathlib/CategoryTheory/Limits/Shapes/SingleObj.lean index 50df41bfc58403..e489e1de66e0f3 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/SingleObj.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/SingleObj.lean @@ -59,8 +59,6 @@ def Types.sections.equivFixedPoints : J.sections ≃ MulAction.fixedPoints M (J.obj (SingleObj.star M)) where toFun s := ⟨s.val _, s.property⟩ invFun p := ⟨fun _ ↦ p.val, p.property⟩ - left_inv _ := rfl - right_inv _ := rfl /-- The limit of `J : SingleObj M ⥤ Type u` is equivalent to the fixed points of the induced action on `J.obj (SingleObj.star M)`. -/ diff --git a/Mathlib/CategoryTheory/Limits/Types/ColimitType.lean b/Mathlib/CategoryTheory/Limits/Types/ColimitType.lean new file mode 100644 index 00000000000000..62530a7ee94465 --- /dev/null +++ b/Mathlib/CategoryTheory/Limits/Types/ColimitType.lean @@ -0,0 +1,272 @@ +/- +Copyright (c) 2025 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +import Mathlib.CategoryTheory.Functor.Basic +import Mathlib.CategoryTheory.Types + +/-! +# The colimit type of a functor to types + +Given a category `J` (with `J : Type u` and `[Category.{v} J]`) and +a functor `F : J ⥤ Type w₀`, we introduce a type `F.ColimitType : Type (max u w₀)`, +which satisfies a certain universal property of the colimit: it is defined +as a suitable quotient of `Σ j, F.obj j`. This universal property is not +expressed in a categorical way (as in general `Type (max u w₀)` +is not the same as `Type u`). + +We also introduce a notion of cocone of `F : J ⥤ Type w₀`, this is `F.CoconeTypes`, +or more precisely `Functor.CoconeTypes.{w₁} F`, which consists of a candidate +colimit type for `F` which is in `Type w₁` (in case `w₁ = w₀`, we shall show +this is the same as the data of `c : Cocone F` in the categorical sense). +Given `c : F.CoconeTypes`, we also introduce a property `c.IsColimit` which says +that the canonical map `F.ColimitType → c.pt` is a bijection, and we shall show (TODO) +that when `w₁ = w₀`, it is equivalent to saying that the corresponding cocone +in a categorical sense is a colimit. + +## TODO +* refactor `DirectedSystem` and the construction of colimits in `Type` +by using `Functor.ColimitType`. +* add a similar API for limits in `Type`? + +-/ + +universe w₃ w₂ w₁ w₀ v u + +assert_not_exists CategoryTheory.Limits.Cocone + +namespace CategoryTheory + +variable {J : Type u} [Category.{v} J] + +namespace Functor + +variable (F : J ⥤ Type w₀) + +/-- Given a functor `F : J ⥤ Type w₀`, this is a "cocone" of `F` where +we allow the point `pt` to be in a different universe than `w`. -/ +structure CoconeTypes where + /-- the point of the cocone -/ + pt : Type w₁ + /-- a family of maps to `pt` -/ + ι (j : J) : F.obj j → pt + ι_naturality {j j' : J} (f : j ⟶ j') : + (ι j').comp (F.map f) = ι j := by aesop + +namespace CoconeTypes + +attribute [simp] ι_naturality + +variable {F} + +@[simp] +lemma ι_naturality_apply (c : CoconeTypes.{w₁} F) {j j' : J} (f : j ⟶ j') (x : F.obj j) : + c.ι j' (F.map f x) = c.ι j x := + congr_fun (c.ι_naturality f) x + +/-- Given `c : F.CoconeTypes` and a map `φ : c.pt → T`, this is +the cocone for `F` obtained by postcomposition with `φ`. -/ +@[simps -fullyApplied] +def postcomp (c : CoconeTypes.{w₁} F) {T : Type w₂} (φ : c.pt → T) : + F.CoconeTypes where + pt := T + ι j := φ.comp (c.ι j) + +end CoconeTypes + +/-- Given `F : J ⥤ Type w₀`, this is the relation `Σ j, F.obj j` which +generates an equivalence relation such that the quotient identifies +to the colimit type of `F`. -/ +def ColimitTypeRel : (Σ j, F.obj j) → (Σ j, F.obj j) → Prop := + fun p p' ↦ ∃ f : p.1 ⟶ p'.1, p'.2 = F.map f p.2 + +/-- The colimit type of a functor `F : J ⥤ Type w₀`. (It may not +be in `Type w₀`.) -/ +def ColimitType : Type (max u w₀) := Quot F.ColimitTypeRel + +/-- The canonical maps `F.obj j → F.ColimitType`. -/ +def ιColimitType (j : J) (x : F.obj j) : F.ColimitType := + Quot.mk _ ⟨j, x⟩ + +lemma ιColimitType_jointly_surjective (t : F.ColimitType) : + ∃ j x, F.ιColimitType j x = t := by + obtain ⟨_, _⟩ := t + exact ⟨_, _, rfl⟩ + +@[simp] +lemma ιColimitType_map {j j' : J} (f : j ⟶ j') (x : F.obj j) : + F.ιColimitType j' (F.map f x) = F.ιColimitType j x := + (Quot.sound ⟨f, rfl⟩).symm + +/-- The cocone corresponding to `F.ColimitType`. -/ +@[simps -fullyApplied] +def coconeTypes : F.CoconeTypes where + pt := F.ColimitType + ι j := F.ιColimitType j + +/-- An heterogeneous universe version of the universal property of the colimit is +satisfied by `F.ColimitType` together the maps `F.ιColimitType j`. -/ +def descColimitType (c : F.CoconeTypes) : F.ColimitType → c.pt := + Quot.lift (fun ⟨j, x⟩ ↦ c.ι j x) (by rintro _ _ ⟨_, _⟩; aesop) + +@[simp] +lemma descColimitType_comp_ι (c : F.CoconeTypes) (j : J) : + (F.descColimitType c).comp (F.ιColimitType j) = c.ι j := rfl + +@[simp] +lemma descColimitType_ιColimitType_apply (c : F.CoconeTypes) (j : J) (x : F.obj j) : + F.descColimitType c (F.ιColimitType j x) = c.ι j x := rfl + +namespace CoconeTypes + +variable {F} (c : CoconeTypes.{w₁} F) + +/-- Given `F : J ⥤ Type w₀` and `c : F.CoconeTypes`, this is the property +that `c` is a colimit. It is defined by saying the canonical map +`F.descColimitType c : F.ColimiType → c.pt` is a bijection. -/ +structure IsColimit : Prop where + bijective : Function.Bijective (F.descColimitType c) + +namespace IsColimit + +variable {c} (hc : c.IsColimit) + +include hc + +/-- Given `F : J ⥤ Type w₀`, and `c : F.CoconeTypes` a cocone that is colimit, +this is the equivalence `F.ColimitType ≃ c.pt`. -/ +@[simps! apply] +noncomputable def equiv : F.ColimitType ≃ c.pt := + Equiv.ofBijective _ hc.bijective + +@[simp] +lemma equiv_symm_ι_apply (j : J) (x : F.obj j) : + hc.equiv.symm (c.ι j x) = F.ιColimitType j x := + hc.equiv.injective (by simp) + +lemma ι_jointly_surjective (y : c.pt) : + ∃ j x, c.ι j x = y := by + obtain ⟨z, rfl⟩ := hc.equiv.surjective y + obtain ⟨j, x, rfl⟩ := F.ιColimitType_jointly_surjective z + exact ⟨j, x, rfl⟩ + +lemma funext {T : Type w₂} {f g : c.pt → T} + (h : ∀ j, f.comp (c.ι j) = g.comp (c.ι j)) : f = g := by + funext y + obtain ⟨j, x, rfl⟩ := hc.ι_jointly_surjective y + exact congr_fun (h j) x + +lemma exists_desc (c' : CoconeTypes.{w₂} F) : + ∃ (f : c.pt → c'.pt), ∀ (j : J), f.comp (c.ι j) = c'.ι j := + ⟨(F.descColimitType c').comp hc.equiv.symm, by aesop⟩ + +/-- If `F : J ⥤ Type w₀` and `c : F.CoconeTypes` is colimit, then +`c` satisfies a heterogeneous universe version of the universal +property of colimits. -/ +noncomputable def desc (c' : CoconeTypes.{w₂} F) : c.pt → c'.pt := + (hc.exists_desc c').choose + +@[simp] +lemma fac (c' : CoconeTypes.{w₂} F) (j : J) : + (hc.desc c').comp (c.ι j) = c'.ι j := + (hc.exists_desc c').choose_spec j + +@[simp] +lemma fac_apply (c' : CoconeTypes.{w₂} F) (j : J) (x : F.obj j) : + hc.desc c' (c.ι j x) = c'.ι j x := + congr_fun (hc.fac c' j) x + +lemma of_equiv {c' : CoconeTypes.{w₂} F} (e : c.pt ≃ c'.pt) + (he : ∀ j x, c'.ι j x = e (c.ι j x)) : c'.IsColimit where + bijective := by + convert Function.Bijective.comp e.bijective hc.bijective + ext y + obtain ⟨j, x, rfl⟩ := F.ιColimitType_jointly_surjective y + aesop + +end IsColimit + +/-- Structure which expresses that `c : F.CoconeTypes` +satisfies the universal property of the colimit of types: +compatible families of maps `F.obj j → T` (where `T` +is any type in a specified universe) descend in a unique +way as maps `c.pt → T`. -/ +structure IsColimitCore where + /-- any cocone descends (in a unique way) as a map -/ + desc (c' : CoconeTypes.{w₂} F) : c.pt → c'.pt + fac (c' : CoconeTypes.{w₂} F) (j : J) : + (desc c').comp (c.ι j) = c'.ι j := by aesop + funext {T : Type w₂} {f g : c.pt → T} + (h : ∀ j, f.comp (c.ι j) = g.comp (c.ι j)) : f = g + +namespace IsColimitCore + +attribute [simp] fac + +variable {c} + +@[simp] +lemma fac_apply (hc : IsColimitCore.{w₂} c) + (c' : CoconeTypes.{w₂} F) (j : J) (x : F.obj j): + hc.desc c' (c.ι j x) = c'.ι j x := + congr_fun (hc.fac c' j) x + +/-- Any structure `IsColimitCore.{max w₂ w₃} c` can be +lowered to `IsColimitCore.{w₂} c` -/ +def down (hc : IsColimitCore.{max w₂ w₃} c) : + IsColimitCore.{w₂} c where + desc c' := Equiv.ulift.toFun.comp + (hc.desc (c'.postcomp Equiv.ulift.{w₃}.symm)) + fac c' j := by + rw [Function.comp_assoc, hc.fac] + rfl + funext {T f g} h := by + suffices Equiv.ulift.{w₃}.invFun.comp f = + Equiv.ulift.invFun.comp g by + ext x + simpa using congr_fun this x + exact hc.funext (fun j ↦ by simp [Function.comp_assoc, h]) + +end IsColimitCore + +variable {c} in +/-- When `c : F.CoconeTypes` satisfies the property +`c.IsColimit`, this is a term in `IsColimitCore.{w₂} c` +for any universe `w₂`. -/ +@[simps] +noncomputable def IsColimit.isColimitCore (hc : c.IsColimit) : + IsColimitCore.{w₂} c where + desc := hc.desc + funext := hc.funext + +lemma IsColimitCore.isColimit (hc : IsColimitCore.{max u w₀ w₁} c) : + c.IsColimit where + bijective := by + let e : F.ColimitType ≃ c.pt := + { toFun := F.descColimitType c + invFun := (down.{max u w₁} hc).desc F.coconeTypes + left_inv y := by + obtain ⟨j, x, rfl⟩ := F.ιColimitType_jointly_surjective y + simp + right_inv := by + have : (F.descColimitType c).comp + ((down.{max u w₁} hc).desc F.coconeTypes) = id := + (down.{max u w₀} hc).funext (fun j ↦ by + rw [Function.id_comp, Function.comp_assoc, fac, + coconeTypes_ι, descColimitType_comp_ι]) + exact congr_fun this } + exact e.bijective + +end CoconeTypes + +lemma isColimit_coconeTypes : F.coconeTypes.IsColimit where + bijective := by + convert Function.bijective_id + ext y + obtain ⟨j, x, rfl⟩ := F.ιColimitType_jointly_surjective y + rfl + +end Functor + +end CategoryTheory diff --git a/Mathlib/CategoryTheory/Limits/Types/Shapes.lean b/Mathlib/CategoryTheory/Limits/Types/Shapes.lean index 679ccfbe4889e7..93804f59838444 100644 --- a/Mathlib/CategoryTheory/Limits/Types/Shapes.lean +++ b/Mathlib/CategoryTheory/Limits/Types/Shapes.lean @@ -668,7 +668,6 @@ noncomputable def isLimitEquivBijective : invFun h := IsLimit.ofIsoLimit (Types.pullbackLimitCone f g).isLimit (Iso.symm (PullbackCone.ext (Equiv.ofBijective _ h).toIso)) left_inv _ := Subsingleton.elim _ _ - right_inv _ := rfl end PullbackCone @@ -949,7 +948,6 @@ def MulticospanIndex.sectionsEquiv : invFun s := { val := fun i ↦ s.val (.left i) property := fun r ↦ (s.property (.fst r)).trans (s.property (.snd r)).symm } - left_inv _ := rfl right_inv s := by ext (_|r) · rfl diff --git a/Mathlib/CategoryTheory/Limits/Types/Yoneda.lean b/Mathlib/CategoryTheory/Limits/Types/Yoneda.lean index d78b40ebb78f80..4d3692aedb4e0f 100644 --- a/Mathlib/CategoryTheory/Limits/Types/Yoneda.lean +++ b/Mathlib/CategoryTheory/Limits/Types/Yoneda.lean @@ -36,8 +36,6 @@ def compCoyonedaSectionsEquiv (F : J ⥤ C) (X : C) : rw [Category.id_comp] exact (s.property f).symm } invFun τ := ⟨τ.app, fun {j j'} f => by simpa using (τ.naturality f).symm⟩ - left_inv _ := rfl - right_inv _ := rfl /-- Sections of `F.op ⋙ yoneda.obj X` identify to natural transformations `F ⟶ (const J).obj X`. -/ @@ -51,8 +49,6 @@ def opCompYonedaSectionsEquiv (F : J ⥤ C) (X : C) : rw [Category.comp_id] exact (s.property f.op) } invFun τ := ⟨fun j => τ.app j.unop, fun {j j'} f => by simp [τ.naturality f.unop]⟩ - left_inv _ := rfl - right_inv _ := rfl /-- Sections of `F ⋙ yoneda.obj X` identify to natural transformations `(const J).obj X ⟶ F`. -/ @@ -67,8 +63,6 @@ def compYonedaSectionsEquiv (F : J ⥤ Cᵒᵖ) (X : C) : exact Quiver.Hom.unop_inj (s.property f).symm } invFun τ := ⟨fun j => (τ.app j).unop, fun {j j'} f => Quiver.Hom.op_inj (by simpa using (τ.naturality f).symm)⟩ - left_inv _ := rfl - right_inv _ := rfl end diff --git a/Mathlib/CategoryTheory/Localization/Construction.lean b/Mathlib/CategoryTheory/Localization/Construction.lean index 1f7e0457b1021e..f717af55fae968 100644 --- a/Mathlib/CategoryTheory/Localization/Construction.lean +++ b/Mathlib/CategoryTheory/Localization/Construction.lean @@ -191,7 +191,6 @@ localization with respect to a morphism_property `W` -/ def objEquiv : C ≃ W.Localization where toFun := W.Q.obj invFun X := X.as.obj - left_inv _ := rfl right_inv := by rintro ⟨⟨X⟩⟩ rfl diff --git a/Mathlib/CategoryTheory/Localization/Resolution.lean b/Mathlib/CategoryTheory/Localization/Resolution.lean index dfe745aa46c4aa..6607506d4909e8 100644 --- a/Mathlib/CategoryTheory/Localization/Resolution.lean +++ b/Mathlib/CategoryTheory/Localization/Resolution.lean @@ -221,17 +221,13 @@ lemma nonempty_leftResolution_iff_op (X₂ : C₂) : Nonempty (Φ.LeftResolution X₂) ↔ Nonempty (Φ.op.RightResolution (Opposite.op X₂)) := Equiv.nonempty_congr { toFun := fun L => L.op - invFun := fun R => R.unop - left_inv := fun _ => rfl - right_inv := fun _ => rfl } + invFun := fun R => R.unop } lemma nonempty_rightResolution_iff_op (X₂ : C₂) : Nonempty (Φ.RightResolution X₂) ↔ Nonempty (Φ.op.LeftResolution (Opposite.op X₂)) := Equiv.nonempty_congr { toFun := fun R => R.op - invFun := fun L => L.unop - left_inv := fun _ => rfl - right_inv := fun _ => rfl } + invFun := fun L => L.unop } lemma hasLeftResolutions_iff_op : Φ.HasLeftResolutions ↔ Φ.op.HasRightResolutions := ⟨fun _ X₂ => ⟨(Classical.arbitrary (Φ.LeftResolution X₂.unop)).op⟩, diff --git a/Mathlib/CategoryTheory/LocallyDirected.lean b/Mathlib/CategoryTheory/LocallyDirected.lean new file mode 100644 index 00000000000000..9d91d02855c701 --- /dev/null +++ b/Mathlib/CategoryTheory/LocallyDirected.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2025 Andrew Yang. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Andrew Yang +-/ +import Mathlib.CategoryTheory.Limits.Shapes.WidePullbacks + +/-! +## Locally directed gluing + +We say that a diagram of sets is "locally directed" if for any `V, W ⊆ U` in the diagram, +`V ∩ W` is a union of elements in the diagram. Equivalently, for every `x ∈ U` in the diagram, +the set of elements containing `x` is directed (and hence the name). + +This is the condition needed to show that a colimit (in `TopCat`) of open embeddings is the +gluing of the open sets. See `Mathlib/AlgebraicGeometry/Gluing.lean` for an actual application. +-/ + +namespace CategoryTheory + +open Limits + +variable {J : Type*} [Category J] + +/-- +We say that a functor `F` to `Type*` is locally directed if for every `x ∈ F.obj k`, the +set of `F.obj` containing `x` is (co)directed. +That is, for each diagram +``` + x ∈ Fₖ + ↗ ↖ +xᵢ ∈ Fᵢ xⱼ ∈ Fⱼ +``` +there exists +``` +xᵢ ∈ Fᵢ xⱼ ∈ Fⱼ + ↖ ↗ + xₗ ∈ Fₗ +``` +that commutes with it. +-/ +class Functor.IsLocallyDirected (F : J ⥤ Type*) : Prop where + cond (F) : ∀ {i j k} (fi : i ⟶ k) (fj : j ⟶ k) (xi : F.obj i) (xj : F.obj j), + F.map fi xi = F.map fj xj → ∃ (l : J) (fli : l ⟶ i) (flj : l ⟶ j) (x : _), + F.map fli x = xi ∧ F.map flj x = xj + +alias Functor.exists_map_eq_of_isLocallyDirected := Functor.IsLocallyDirected.cond + +instance (F : Discrete J ⥤ Type*) : F.IsLocallyDirected := by + constructor + rintro ⟨i⟩ ⟨j⟩ ⟨k⟩ ⟨⟨⟨⟩⟩⟩ ⟨⟨⟨⟩⟩⟩ + simp only [Discrete.functor_map_id, types_id_apply, forall_eq'] + exact fun x ↦ ⟨⟨i⟩, 𝟙 _, 𝟙 _, x, by simp⟩ + +instance (F : WidePushoutShape J ⥤ Type*) [∀ i, Mono (F.map (.init i))] : + F.IsLocallyDirected := by + constructor + rintro i j k (_ | i) (_ | j) + · simp only [WidePushoutShape.hom_id, FunctorToTypes.map_id_apply, forall_eq'] + exact fun x ↦ ⟨_, 𝟙 _, 𝟙 _, x, by simp⟩ + · simp only [WidePushoutShape.hom_id, FunctorToTypes.map_id_apply, forall_comm, forall_eq] + exact fun x ↦ ⟨_, .init _, 𝟙 _, x, by simp⟩ + · simp only [WidePushoutShape.hom_id, FunctorToTypes.map_id_apply, forall_eq'] + exact fun x ↦ ⟨_, 𝟙 _, .init _, x, by simp⟩ + · simp only [((CategoryTheory.mono_iff_injective (F.map (.init i))).mp inferInstance).eq_iff, + forall_eq'] + exact fun x ↦ ⟨_, 𝟙 _, 𝟙 _, x, by simp⟩ + +end CategoryTheory diff --git a/Mathlib/CategoryTheory/Monad/Monadicity.lean b/Mathlib/CategoryTheory/Monad/Monadicity.lean index 73f76b4f8fbfda..f4253485777ac4 100644 --- a/Mathlib/CategoryTheory/Monad/Monadicity.lean +++ b/Mathlib/CategoryTheory/Monad/Monadicity.lean @@ -103,9 +103,7 @@ def comparisonLeftAdjointHomEquiv (A : adj.toMonad.Algebra) (B : D) { toFun := fun g => { f := _ h := g.prop } - invFun := fun f => ⟨f.f, f.h⟩ - left_inv := fun g => by ext; rfl - right_inv := fun f => by ext; rfl } + invFun := fun f => ⟨f.f, f.h⟩ } /-- Construct the adjunction to the comparison functor. -/ diff --git a/Mathlib/CategoryTheory/Monoidal/Bimon_.lean b/Mathlib/CategoryTheory/Monoidal/Bimon_.lean index 7a5bd01c0d9f3b..bd164fbd33166e 100644 --- a/Mathlib/CategoryTheory/Monoidal/Bimon_.lean +++ b/Mathlib/CategoryTheory/Monoidal/Bimon_.lean @@ -144,16 +144,13 @@ theorem ofMon_Comon_ObjX_mul (M : Mon_ (Comon_ C)) : μ[(ofMon_Comon_ObjX M).X] = 𝟙 (M.X.X ⊗ M.X.X) ≫ μ[M.X].hom := rfl +attribute [local simp] Mon_Class.tensorObj.one_def Mon_Class.tensorObj.mul_def tensorμ in /-- The object level part of the backward direction of `Comon_ (Mon_ C) ≌ Mon_ (Comon_ C)` -/ @[simps] def ofMon_Comon_Obj (M : Mon_ (Comon_ C)) : Bimon_ C where X := ofMon_Comon_ObjX M - comon := - { counit := - { hom := ε[M.X.X] } - comul := - { hom := Δ[M.X.X] - mul_hom := by simp [tensorμ] } } + comon.counit.hom := ε[M.X.X] + comon.comul.hom := Δ[M.X.X] variable (C) in /-- The backward direction of `Comon_ (Mon_ C) ≌ Mon_ (Comon_ C)` -/ @@ -277,6 +274,7 @@ instance (M : Bimon_ C) : Bimon_Class M.X.X where comul_assoc' := by simp_rw [← Bimon_ClassAux_comul, Comon_Class.comul_assoc] +attribute [local simp] Mon_Class.tensorObj.one_def in @[reassoc] theorem one_comul (M : C) [Bimon_Class M] : η[M] ≫ Δ[M] = (λ_ _).inv ≫ (η[M] ⊗ η[M]) := by diff --git a/Mathlib/CategoryTheory/Monoidal/Hopf_.lean b/Mathlib/CategoryTheory/Monoidal/Hopf_.lean index 7f0acea95cfdc6..c5e3558d280185 100644 --- a/Mathlib/CategoryTheory/Monoidal/Hopf_.lean +++ b/Mathlib/CategoryTheory/Monoidal/Hopf_.lean @@ -163,7 +163,7 @@ theorem antipode_comul₁ (A : C) [Hopf_Class A] : rw [Bimon_.compatibility] slice_lhs 1 3 => rw [antipode_left] - simp + simp [Mon_Class.tensorObj.one_def] /-- Auxiliary calculation for `antipode_comul`. diff --git a/Mathlib/CategoryTheory/Monoidal/Mon_.lean b/Mathlib/CategoryTheory/Monoidal/Mon_.lean index 86e9273242790c..94e38ec370cf42 100644 --- a/Mathlib/CategoryTheory/Monoidal/Mon_.lean +++ b/Mathlib/CategoryTheory/Monoidal/Mon_.lean @@ -626,7 +626,9 @@ theorem mul_rightUnitor {M : C} [Mon_Class M] : namespace tensorObj -@[simps] +-- We don't want `tensorObj.one_def` to be simp as it would loop with `IsMon_Hom.one_hom` applied +-- to `(λ_ N.X).inv`. +@[simps -isSimp] instance {M N : C} [Mon_Class M] [Mon_Class N] : Mon_Class (M ⊗ N) where one := (λ_ (𝟙_ C)).inv ≫ (η ⊗ η) mul := tensorμ M N M N ≫ (μ ⊗ μ) @@ -642,10 +644,10 @@ variable {X Y Z W : C} [Mon_Class X] [Mon_Class Y] [Mon_Class Z] [Mon_Class W] instance {f : X ⟶ Y} {g : Z ⟶ W} [IsMon_Hom f] [IsMon_Hom g] : IsMon_Hom (f ⊗ g) where one_hom := by - dsimp + dsimp [tensorObj.one_def] slice_lhs 2 3 => rw [← tensor_comp, one_hom, one_hom] mul_hom := by - dsimp + dsimp [tensorObj.mul_def] slice_rhs 1 2 => rw [tensorμ_natural] slice_lhs 2 3 => rw [← tensor_comp, mul_hom, mul_hom, tensor_comp] simp only [Category.assoc] @@ -749,7 +751,9 @@ theorem tensor_mul (M N : Mon_ C) : μ[(M ⊗ N).X] = instance monMonoidal : MonoidalCategory (Mon_ C) where tensorHom_def := by intros; ext; simp [tensorHom_def] -@[simps!] +-- We don't want `tensorObj.one_def` to be simp as it would loop with `IsMon_Hom.one_hom` applied +-- to `(λ_ N.X).inv`. +@[simps! -isSimp] instance {M N : C} [Mon_Class M] [Mon_Class N] : Mon_Class (M ⊗ N) := inferInstanceAs <| Mon_Class (Mon_.mk M ⊗ Mon_.mk N).X @@ -788,7 +792,7 @@ namespace Mon_Class theorem mul_braiding (X Y : C) [Mon_Class X] [Mon_Class Y] : μ ≫ (β_ X Y).hom = ((β_ X Y).hom ⊗ (β_ X Y).hom) ≫ μ := by - dsimp + dsimp [tensorObj.mul_def] simp only [tensorμ, Category.assoc, BraidedCategory.braiding_naturality, BraidedCategory.braiding_tensor_right, BraidedCategory.braiding_tensor_left, comp_whiskerRight, whisker_assoc, MonoidalCategory.whiskerLeft_comp, pentagon_assoc, diff --git a/Mathlib/CategoryTheory/Opposites.lean b/Mathlib/CategoryTheory/Opposites.lean index 6db64f2f3c3baf..7c7ac385a584dd 100644 --- a/Mathlib/CategoryTheory/Opposites.lean +++ b/Mathlib/CategoryTheory/Opposites.lean @@ -743,8 +743,6 @@ def opEquiv''' (A B : C) : (Opposite.op A ⟶ Opposite.op B) ≃ (B ⟶ A) := def opEquiv (A B : Cᵒᵖ) : (A ⟶ B) ≃ (B.unop ⟶ A.unop) where toFun f := f.unop invFun g := g.op - left_inv _ := rfl - right_inv _ := rfl instance subsingleton_of_unop (A B : Cᵒᵖ) [Subsingleton (unop B ⟶ unop A)] : Subsingleton (A ⟶ B) := (opEquiv A B).subsingleton @@ -763,12 +761,6 @@ Note this is definitionally the same as the other three variants: def isoOpEquiv (A B : Cᵒᵖ) : (A ≅ B) ≃ (B.unop ≅ A.unop) where toFun f := f.unop invFun g := g.op - left_inv _ := by - ext - rfl - right_inv _ := by - ext - rfl namespace Functor diff --git a/Mathlib/CategoryTheory/Sites/Coherent/ExtensiveSheaves.lean b/Mathlib/CategoryTheory/Sites/Coherent/ExtensiveSheaves.lean index 15f062c9177004..42a0a48dcd7086 100644 --- a/Mathlib/CategoryTheory/Sites/Coherent/ExtensiveSheaves.lean +++ b/Mathlib/CategoryTheory/Sites/Coherent/ExtensiveSheaves.lean @@ -54,7 +54,7 @@ theorem isSheafFor_extensive_of_preservesFiniteProducts {X : C} (S : Presieve X) have : (ofArrows Z (Cofan.mk X π).inj).hasPullbacks := (inferInstance : (ofArrows Z π).hasPullbacks) cases nonempty_fintype α - exact isSheafFor_of_preservesProduct _ _ hc + exact isSheafFor_of_preservesProduct F _ hc instance {α : Type} [Finite α] (Z : α → C) : (ofArrows Z (fun i ↦ Sigma.ι Z i)).Extensive := ⟨⟨α, inferInstance, Z, (fun i ↦ Sigma.ι Z i), rfl, ⟨coproductIsCoproduct _⟩⟩⟩ diff --git a/Mathlib/CategoryTheory/Sites/EqualizerSheafCondition.lean b/Mathlib/CategoryTheory/Sites/EqualizerSheafCondition.lean index 428a8a472e9710..9e2a6ce3e0e660 100644 --- a/Mathlib/CategoryTheory/Sites/EqualizerSheafCondition.lean +++ b/Mathlib/CategoryTheory/Sites/EqualizerSheafCondition.lean @@ -30,7 +30,7 @@ equalizer diagrams. -/ -universe w v u +universe t w v u namespace CategoryTheory @@ -254,9 +254,8 @@ variable (P : Cᵒᵖ ⥤ Type w) {X : C} (R : Presieve X) (S : Sieve X) open Presieve -variable {B : C} {I : Type} (X : I → C) (π : (i : I) → X i ⟶ B) +variable {B : C} {I : Type t} [Small.{w} I] (X : I → C) (π : (i : I) → X i ⟶ B) [(Presieve.ofArrows X π).hasPullbacks] --- TODO: allow `I : Type w` /-- The middle object of the fork diagram of the Stacks entry. @@ -316,9 +315,13 @@ theorem w : forkMap P X π ≫ firstMap P X π = forkMap P X π ≫ secondMap P /-- The family of elements given by `x : FirstObj P S` is compatible iff `firstMap` and `secondMap` map it to the same point. +See `CategoryTheory.Equalizer.Presieve.Arrows.compatible_iff_of_small` for a version with +less universe assumptions. -/ -theorem compatible_iff (x : FirstObj P X) : (Arrows.Compatible P π ((Types.productIso _).hom x)) ↔ - firstMap P X π x = secondMap P X π x := by +theorem compatible_iff {I : Type w} (X : I → C) (π : (i : I) → X i ⟶ B) + [(Presieve.ofArrows X π).hasPullbacks] (x : FirstObj P X) : + (Arrows.Compatible P π ((Types.productIso _).hom x)) ↔ + firstMap P X π x = secondMap P X π x := by rw [Arrows.pullbackCompatible_iff] constructor · intro t @@ -328,18 +331,32 @@ theorem compatible_iff (x : FirstObj P X) : (Arrows.Compatible P π ((Types.prod apply_fun Pi.π (fun (ij : I × I) ↦ P.obj (op (pullback (π ij.1) (π ij.2)))) ⟨i, j⟩ at t simpa [firstMap, secondMap] using t +/-- Version of `CategoryTheory.Equalizer.Presieve.Arrows.compatible_iff` for a small +indexing type. -/ +lemma compatible_iff_of_small (x : FirstObj P X) : + (Arrows.Compatible P π ((equivShrink _).symm ((Types.Small.productIso _).hom x))) ↔ + firstMap P X π x = secondMap P X π x := by + rw [Arrows.pullbackCompatible_iff] + refine ⟨fun t ↦ ?_, fun t i j ↦ ?_⟩ + · ext ij + simpa [firstMap, secondMap] using t ij.1 ij.2 + · apply_fun Pi.π (fun (ij : I × I) ↦ P.obj (op (pullback (π ij.1) (π ij.2)))) ⟨i, j⟩ at t + simpa [firstMap, secondMap] using t + /-- `P` is a sheaf for `Presieve.ofArrows X π`, iff the fork given by `w` is an equalizer. -/ @[stacks 00VM] theorem sheaf_condition : (Presieve.ofArrows X π).IsSheafFor P ↔ Nonempty (IsLimit (Fork.ofι (forkMap P X π) (w P X π))) := by rw [Types.type_equalizer_iff_unique, isSheafFor_arrows_iff] - erw [← Equiv.forall_congr_right (Types.productIso _).toEquiv.symm] - simp_rw [← compatible_iff, ← Iso.toEquiv_fun, Equiv.apply_symm_apply] + simp only [FirstObj] + rw [← Equiv.forall_congr_right ((equivShrink _).trans (Types.Small.productIso _).toEquiv.symm)] + simp_rw [← compatible_iff_of_small, ← Iso.toEquiv_fun, Equiv.trans_apply, Equiv.apply_symm_apply, + Equiv.symm_apply_apply] apply forall₂_congr intro x _ apply existsUnique_congr intro t - erw [Equiv.eq_symm_apply] + rw [Equiv.eq_symm_apply, ← Equiv.symm_apply_eq] constructor · intro q funext i @@ -350,6 +367,32 @@ theorem sheaf_condition : (Presieve.ofArrows X π).IsSheafFor P ↔ end Arrows +/-- The sheaf condition for a single morphism is the same as the canonical fork diagram being +limiting. -/ +lemma isSheafFor_singleton_iff {F : Cᵒᵖ ⥤ Type*} {X Y : C} {f : X ⟶ Y} + (c : PullbackCone f f) (hc : IsLimit c) : + Presieve.IsSheafFor F (.singleton f) ↔ + Nonempty + (IsLimit (Fork.ofι (F.map f.op) (f := F.map c.fst.op) (g := F.map c.snd.op) + (by simp [← Functor.map_comp, ← op_comp, c.condition]))) := by + have h (x : F.obj (op X)) : (∀ {Z : C} (p₁ p₂ : Z ⟶ X), + p₁ ≫ f = p₂ ≫ f → F.map p₁.op x = F.map p₂.op x) ↔ F.map c.fst.op x = F.map c.snd.op x := by + refine ⟨fun H ↦ H _ _ c.condition, fun H Z p₁ p₂ h ↦ ?_⟩ + rw [← PullbackCone.IsLimit.lift_fst hc _ _ h, op_comp, FunctorToTypes.map_comp_apply, H] + simp [← FunctorToTypes.map_comp_apply, ← op_comp] + rw [Types.type_equalizer_iff_unique, Presieve.isSheafFor_singleton] + simp_rw [h] + +/-- Special case of `isSheafFor_singleton_iff` with `c = pullback.cone f f`. -/ +lemma isSheafFor_singleton_iff_of_hasPullback {F : Cᵒᵖ ⥤ Type*} {X Y : C} {f : X ⟶ Y} + [HasPullback f f] : + Presieve.IsSheafFor F (.singleton f) ↔ + Nonempty + (IsLimit (Fork.ofι (F.map f.op) (f := F.map (pullback.fst f f).op) + (g := F.map (pullback.snd f f).op) + (by simp [← Functor.map_comp, ← op_comp, pullback.condition]))) := + isSheafFor_singleton_iff (pullback.cone f f) (pullback.isLimit f f) + end Presieve end diff --git a/Mathlib/CategoryTheory/Sites/IsSheafFor.lean b/Mathlib/CategoryTheory/Sites/IsSheafFor.lean index 93fc8d62a605b7..2422bb809bf34f 100644 --- a/Mathlib/CategoryTheory/Sites/IsSheafFor.lean +++ b/Mathlib/CategoryTheory/Sites/IsSheafFor.lean @@ -93,6 +93,13 @@ def FamilyOfElements (P : Cᵒᵖ ⥤ Type w) (R : Presieve X) := instance : Inhabited (FamilyOfElements P (⊥ : Presieve X)) := ⟨fun _ _ => False.elim⟩ +@[ext] +lemma FamilyOfElements.ext {R : Presieve X} {x y : R.FamilyOfElements P} + (H : ∀ {Y : C} (f : Y ⟶ X) (hf : R f), x f hf = y f hf) : + x = y := by + funext Z f hf + exact H f hf + /-- A family of elements for a presheaf on the presieve `R₂` can be restricted to a smaller presieve `R₁`. -/ @@ -113,6 +120,21 @@ lemma FamilyOfElements.restrict_map (p : FamilyOfElements P R) (φ : P ⟶ Q) {R' : Presieve X} (h : R' ≤ R) : (p.restrict h).map φ = (p.map φ).restrict h := rfl +variable (P) in +/-- A family of elements on `{ f : X ⟶ Y }` is an element of `F(X)`. -/ +@[simps apply, simps -isSimp symm_apply] +def FamilyOfElements.singletonEquiv {X Y : C} (f : X ⟶ Y) : + (singleton f).FamilyOfElements P ≃ P.obj (op X) where + toFun x := x f (by simp) + invFun x Z g hg := P.map (eqToHom <| by cases hg; rfl).op x + left_inv x := by ext _ _ ⟨rfl⟩; simp + right_inv x := by simp + +@[simp] +lemma FamilyOfElements.singletonEquiv_symm_apply_self {X Y : C} (f : X ⟶ Y) (x : P.obj (op X)) : + (singletonEquiv P f).symm x f ⟨⟩ = x := by + simp [singletonEquiv_symm_apply] + /-- A family of elements for the arrow set `R` is *compatible* if for any `f₁ : Y₁ ⟶ X` and `f₂ : Y₂ ⟶ X` in `R`, and any `g₁ : Z ⟶ Y₁` and `g₂ : Z ⟶ Y₂`, if the square `g₁ ≫ f₁ = g₂ ≫ f₂` commutes then the elements of `P Z` obtained by restricting the element of `P Y₁` along `g₁` and @@ -267,6 +289,14 @@ theorem FamilyOfElements.comp_of_compatible (S : Sieve X) {x : FamilyOfElements x (g ≫ f) (S.downward_closed hf g) = P.map g.op (x f hf) := by simpa using t (𝟙 _) g (S.downward_closed hf g) hf (id_comp _) +lemma FamilyOfElements.compatible_singleton_iff + {X Y : C} (f : X ⟶ Y) (x : (singleton f).FamilyOfElements P) : + x.Compatible ↔ ∀ {Z : C} (p₁ p₂ : Z ⟶ X), p₁ ≫ f = p₂ ≫ f → + P.map p₁.op (x f ⟨⟩) = P.map p₂.op (x f ⟨⟩) := by + refine ⟨fun H Z p₁ p₂ h ↦ H _ _ _ _ h, fun H Y₁ Y₂ Z g₁ g₂ f₁ f₂ ↦ ?_⟩ + rintro ⟨rfl⟩ ⟨rfl⟩ h + exact H _ _ h + section FunctorPullback variable {D : Type u₂} [Category.{v₂} D] (F : D ⥤ C) {Z : D} @@ -372,6 +402,14 @@ theorem isAmalgamation_sieveExtend {R : Presieve X} (x : FamilyOfElements P R) ( dsimp [FamilyOfElements.sieveExtend] rw [← ht _, ← FunctorToTypes.map_comp_apply, ← op_comp, hf.choose_spec.choose_spec.choose_spec.2] +@[simp] +lemma FamilyOfElements.isAmalgamation_singleton_iff {X Y : C} (f : X ⟶ Y) + (x : (singleton f).FamilyOfElements P) (y : P.obj (op Y)) : + x.IsAmalgamation y ↔ P.map f.op y = x f ⟨⟩ := by + refine ⟨fun H ↦ H _ _, ?_⟩ + rintro H Y g ⟨rfl⟩ + exact H + /-- A presheaf is separated for a presieve if there is at most one amalgamation. -/ def IsSeparatedFor (P : Cᵒᵖ ⥤ Type w) (R : Presieve X) : Prop := ∀ (x : FamilyOfElements P R) (t₁ t₂), x.IsAmalgamation t₁ → x.IsAmalgamation t₂ → t₁ = t₂ @@ -757,6 +795,24 @@ theorem isSheafFor_arrows_iff_pullbacks : (ofArrows X π).IsSheafFor P ↔ end Arrows +@[simp] +lemma isSeparatedFor_singleton {X Y : C} {f : X ⟶ Y} : + Presieve.IsSeparatedFor P (.singleton f) ↔ + Function.Injective (P.map f.op) := by + rw [IsSeparatedFor, Equiv.forall_congr_left (Presieve.FamilyOfElements.singletonEquiv P f)] + simp_rw [FamilyOfElements.isAmalgamation_singleton_iff, + FamilyOfElements.singletonEquiv_symm_apply_self, Function.Injective] + aesop + +lemma isSheafFor_singleton {X Y : C} {f : X ⟶ Y} : + Presieve.IsSheafFor P (.singleton f) ↔ + ∀ (x : P.obj (op X)), + (∀ {Z : C} (p₁ p₂ : Z ⟶ X), p₁ ≫ f = p₂ ≫ f → P.map p₁.op x = P.map p₂.op x) → + ∃! y, P.map f.op y = x := by + rw [IsSheafFor, Equiv.forall_congr_left (Presieve.FamilyOfElements.singletonEquiv P f)] + simp_rw [FamilyOfElements.compatible_singleton_iff, + FamilyOfElements.isAmalgamation_singleton_iff, FamilyOfElements.singletonEquiv_symm_apply_self] + end Presieve end CategoryTheory diff --git a/Mathlib/CategoryTheory/Sites/Preserves.lean b/Mathlib/CategoryTheory/Sites/Preserves.lean index 35605683ea7f86..9c14f66fdc5b21 100644 --- a/Mathlib/CategoryTheory/Sites/Preserves.lean +++ b/Mathlib/CategoryTheory/Sites/Preserves.lean @@ -73,7 +73,7 @@ section Product variable (hI : IsInitial I) -- This is the data of a particular disjoint coproduct in `C`. -variable {α : Type} {X : α → C} (c : Cofan X) (hc : IsColimit c) +variable {α : Type*} [Small.{w} α] {X : α → C} (c : Cofan X) (hc : IsColimit c) theorem piComparison_fac : have : HasCoproduct X := ⟨⟨c, hc⟩⟩ diff --git a/Mathlib/CategoryTheory/Sites/Pretopology.lean b/Mathlib/CategoryTheory/Sites/Pretopology.lean index 93edd7f0fee9be..3314c7a34c31ec 100644 --- a/Mathlib/CategoryTheory/Sites/Pretopology.lean +++ b/Mathlib/CategoryTheory/Sites/Pretopology.lean @@ -128,11 +128,9 @@ theorem mem_toGrothendieck (K : Pretopology C) (X S) : See [MM92] Chapter III, Section 2, Equations (3,4). -/ def ofGrothendieck (J : GrothendieckTopology C) : Pretopology C where - coverings X R := Sieve.generate R ∈ J X + coverings X := {R | Sieve.generate R ∈ J X} has_isos X Y f i := J.covering_of_eq_top (by simp) - pullbacks X Y f R hR := by - simp only [Set.mem_def, Sieve.pullbackArrows_comm] - apply J.pullback_stable f hR + pullbacks X Y f R hR := by simpa [Sieve.pullbackArrows_comm] using J.pullback_stable f hR transitive X S Ti hS hTi := by apply J.transitive hS intro Y f diff --git a/Mathlib/CategoryTheory/Sites/Sheaf.lean b/Mathlib/CategoryTheory/Sites/Sheaf.lean index 0bc9b9cb5589ca..c083eb99c20fd5 100644 --- a/Mathlib/CategoryTheory/Sites/Sheaf.lean +++ b/Mathlib/CategoryTheory/Sites/Sheaf.lean @@ -106,8 +106,6 @@ def conesEquivSieveCompatibleFamily : rw [id_comp] convert rfl rw [Over.w] } - left_inv _ := rfl - right_inv _ := rfl variable {P S E} variable {x : FamilyOfElements (P ⋙ coyoneda.obj E) S.arrows} (hx : SieveCompatible x) @@ -126,8 +124,6 @@ def homEquivAmalgamation : (hx.cone ⟶ P.mapCone S.arrows.cocone.op) ≃ { t // x.IsAmalgamation t } where toFun l := ⟨l.hom, fun _ f hf => l.w (op ⟨Over.mk f, hf⟩)⟩ invFun t := ⟨t.1, fun f => t.2 f.unop.1.hom f.unop.2⟩ - left_inv _ := rfl - right_inv _ := rfl variable (P S) diff --git a/Mathlib/CategoryTheory/Sites/SheafHom.lean b/Mathlib/CategoryTheory/Sites/SheafHom.lean index 1b798e48eb161c..23519aae65bbed 100644 --- a/Mathlib/CategoryTheory/Sites/SheafHom.lean +++ b/Mathlib/CategoryTheory/Sites/SheafHom.lean @@ -92,7 +92,6 @@ def presheafHomSectionsEquiv : (presheafHom F G).sections ≃ (F ⟶ G) where dsimp at H ⊢ rw [← H] apply presheafHom_map_app_op_mk_id - right_inv _ := rfl variable {F G} diff --git a/Mathlib/CategoryTheory/Whiskering.lean b/Mathlib/CategoryTheory/Whiskering.lean index 6a731bece3d78d..f78379d792668c 100644 --- a/Mathlib/CategoryTheory/Whiskering.lean +++ b/Mathlib/CategoryTheory/Whiskering.lean @@ -3,7 +3,7 @@ Copyright (c) 2018 Kim Morrison. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Kim Morrison -/ -import Mathlib.CategoryTheory.Iso +import Mathlib.Tactic.CategoryTheory.IsoReassoc import Mathlib.CategoryTheory.Functor.Category import Mathlib.CategoryTheory.Functor.FullyFaithful @@ -263,16 +263,17 @@ instance isIso_whiskerRight {G H : C ⥤ D} (α : G ⟶ H) (F : D ⥤ E) [IsIso IsIso (whiskerRight α F) := (isoWhiskerRight (asIso α) F).isIso_hom -@[simp] +@[simp, reassoc] theorem isoWhiskerLeft_trans (F : C ⥤ D) {G H K : D ⥤ E} (α : G ≅ H) (β : H ≅ K) : isoWhiskerLeft F (α ≪≫ β) = isoWhiskerLeft F α ≪≫ isoWhiskerLeft F β := rfl -@[simp] +@[simp, reassoc] theorem isoWhiskerRight_trans {G H K : C ⥤ D} (α : G ≅ H) (β : H ≅ K) (F : D ⥤ E) : isoWhiskerRight (α ≪≫ β) F = isoWhiskerRight α F ≪≫ isoWhiskerRight β F := ((whiskeringRight C D E).obj F).mapIso_trans α β +@[reassoc] theorem isoWhiskerLeft_trans_isoWhiskerRight {F G : C ⥤ D} {H K : D ⥤ E} (α : F ≅ G) (β : H ≅ K) : isoWhiskerLeft F β ≪≫ isoWhiskerRight α K = isoWhiskerRight α H ≪≫ isoWhiskerLeft G β := by ext @@ -304,18 +305,20 @@ theorem isoWhiskerLeft_twice (F : B ⥤ C) (G : C ⥤ D) {H K : D ⥤ E} (α : H (Functor.associator _ _ _).symm ≪≫ isoWhiskerLeft (F ⋙ G) α ≪≫ Functor.associator _ _ _ := by aesop_cat -@[simp] +@[simp, reassoc] theorem isoWhiskerRight_twice {H K : B ⥤ C} (F : C ⥤ D) (G : D ⥤ E) (α : H ≅ K) : isoWhiskerRight (isoWhiskerRight α F) G = Functor.associator _ _ _ ≪≫ isoWhiskerRight α (F ⋙ G) ≪≫ (Functor.associator _ _ _).symm := by aesop_cat +@[reassoc] theorem isoWhiskerRight_left (F : B ⥤ C) {G H : C ⥤ D} (α : G ≅ H) (K : D ⥤ E) : isoWhiskerRight (isoWhiskerLeft F α) K = Functor.associator _ _ _ ≪≫ isoWhiskerLeft F (isoWhiskerRight α K) ≪≫ Functor.associator _ _ _ := by aesop_cat +@[reassoc] theorem isoWhiskerLeft_right (F : B ⥤ C) {G H : C ⥤ D} (α : G ≅ H) (K : D ⥤ E) : isoWhiskerLeft F (isoWhiskerRight α K) = (Functor.associator _ _ _).symm ≪≫ isoWhiskerRight (isoWhiskerLeft F α) K ≪≫ @@ -332,10 +335,12 @@ variable {A : Type u₁} [Category.{v₁} A] {B : Type u₂} [Category.{v₂} B] {C : Type u₃} [Category.{v₃} C] {D : Type u₄} [Category.{v₄} D] {E : Type u₅} [Category.{v₅} E] (F : A ⥤ B) (G : B ⥤ C) (H : C ⥤ D) (K : D ⥤ E) +@[reassoc] theorem triangleIso : associator F (𝟭 B) G ≪≫ isoWhiskerLeft F (leftUnitor G) = isoWhiskerRight (rightUnitor F) G := by aesop_cat +@[reassoc] theorem pentagonIso : isoWhiskerRight (associator F G H) K ≪≫ associator F (G ⋙ H) K ≪≫ isoWhiskerLeft F (associator G H K) = diff --git a/Mathlib/CategoryTheory/Yoneda.lean b/Mathlib/CategoryTheory/Yoneda.lean index a00a78fefd8939..7a126aaf0f759c 100644 --- a/Mathlib/CategoryTheory/Yoneda.lean +++ b/Mathlib/CategoryTheory/Yoneda.lean @@ -268,8 +268,6 @@ def representableByEquiv {F : Cᵒᵖ ⥤ Type v₁} {Y : C} : invFun e := { homEquiv := (e.app _).toEquiv homEquiv_comp := fun {X X'} f g ↦ congr_fun (e.hom.naturality f.op) g } - left_inv _ := rfl - right_inv _ := rfl /-- The isomorphism `yoneda.obj Y ≅ F` induced by `e : F.RepresentableBy Y`. -/ def RepresentableBy.toIso {F : Cᵒᵖ ⥤ Type v₁} {Y : C} (e : F.RepresentableBy Y) : @@ -286,8 +284,6 @@ def corepresentableByEquiv {F : C ⥤ Type v₁} {X : C} : invFun e := { homEquiv := (e.app _).toEquiv homEquiv_comp := fun {X X'} f g ↦ congr_fun (e.hom.naturality f) g } - left_inv _ := rfl - right_inv _ := rfl /-- The isomorphism `coyoneda.obj (op X) ≅ F` induced by `e : F.CorepresentableBy X`. -/ def CorepresentableBy.toIso {F : C ⥤ Type v₁} {X : C} (e : F.CorepresentableBy X) : @@ -855,7 +851,6 @@ def Functor.sectionsEquivHom (F : C ⥤ Type u₂) (X : Type u₂) [Unique X] : { app j x := s.1 j naturality _ _ _ := by ext x; simp } invFun τ := ⟨fun j ↦ τ.app _ (default : X), fun φ ↦ (congr_fun (τ.naturality φ) _).symm⟩ - left_inv s := rfl right_inv τ := by ext _ (x : X) rw [Unique.eq_default x] diff --git a/Mathlib/Combinatorics/Configuration.lean b/Mathlib/Combinatorics/Configuration.lean index ab8bee5fc1bed6..7c465a4da9c695 100644 --- a/Mathlib/Combinatorics/Configuration.lean +++ b/Mathlib/Combinatorics/Configuration.lean @@ -433,7 +433,6 @@ theorem card_points [Fintype P] [Finite L] : Fintype.card P = order P L ^ 2 + or let ϕ : { q // q ≠ p } ≃ Σl : { l : L // p ∈ l }, { q // q ∈ l.1 ∧ q ≠ p } := { toFun := fun q => ⟨⟨mkLine q.2, (mkLine_ax q.2).2⟩, q, (mkLine_ax q.2).1, q.2⟩ invFun := fun lq => ⟨lq.2, lq.2.2.2⟩ - left_inv := fun q => Subtype.ext rfl right_inv := fun lq => Sigma.subtype_ext (Subtype.ext diff --git a/Mathlib/Combinatorics/Quiver/Basic.lean b/Mathlib/Combinatorics/Quiver/Basic.lean index cfd2b0999fb2b5..9e95b66dca4137 100644 --- a/Mathlib/Combinatorics/Quiver/Basic.lean +++ b/Mathlib/Combinatorics/Quiver/Basic.lean @@ -65,8 +65,6 @@ def Hom.opEquiv {V} [Quiver V] {X Y : V} : (X ⟶ Y) ≃ (Opposite.op Y ⟶ Opposite.op X) where toFun := Opposite.op invFun := Opposite.unop - left_inv _ := rfl - right_inv _ := rfl /-- A type synonym for a quiver with no arrows. -/ def Empty (V : Type u) : Type u := V diff --git a/Mathlib/Combinatorics/Quiver/Path.lean b/Mathlib/Combinatorics/Quiver/Path.lean index d268d8b7a9cd97..d21596b9b9a02f 100644 --- a/Mathlib/Combinatorics/Quiver/Path.lean +++ b/Mathlib/Combinatorics/Quiver/Path.lean @@ -193,6 +193,75 @@ theorem toList_injective (a : V) : ∀ b, Injective (toList : Path a b → List theorem toList_inj {p q : Path a b} : p.toList = q.toList ↔ p = q := (toList_injective _ _).eq_iff + +section BoundedPath + +variable {V : Type*} [Quiver V] + +/-- A bounded path is a path with a uniform bound on its length. -/ +def BoundedPaths (v w : V) (n : ℕ) : Sort _ := + { p : Path v w // p.length ≤ n } + +/-- Bounded paths of length zero between two vertices form a subsingleton. -/ +instance instSubsingletonBddPaths (v w : V) : Subsingleton (BoundedPaths v w 0) where + allEq := fun ⟨p, hp⟩ ⟨q, hq⟩ => + match v, w, p, q with + | _, _, .nil, .nil => rfl + | _, _, .cons _ _, _ => by simp [Quiver.Path.length] at hp + | _, _, _, .cons _ _ => by simp [Quiver.Path.length] at hq + +/-- Bounded paths of length zero between two vertices have decidable equality. -/ +def decidableEqBddPathsZero (v w : V) : DecidableEq (BoundedPaths v w 0) := + fun _ _ => isTrue <| Subsingleton.elim _ _ + +/-- Given decidable equality on paths of length up to `n`, we can construct +decidable equality on paths of length up to `n + 1`. -/ +def decidableEqBddPathsOfDecidableEq (n : ℕ) (h₁ : DecidableEq V) + (h₂ : ∀ (v w : V), DecidableEq (v ⟶ w)) (h₃ : ∀ (v w : V), DecidableEq (BoundedPaths v w n)) + (v w : V) : DecidableEq (BoundedPaths v w (n + 1)) := + fun ⟨p, hp⟩ ⟨q, hq⟩ => + match v, w, p, q with + | _, _, .nil, .nil => isTrue rfl + | _, _, .nil, .cons _ _ => isFalse fun h => Quiver.Path.noConfusion <| Subtype.mk.inj h + | _, _, .cons _ _, .nil => isFalse fun h => Quiver.Path.noConfusion <| Subtype.mk.inj h + | _, _, .cons (b := v') p' α, .cons (b := v'') q' β => + match v', v'', h₁ v' v'' with + | _, _, isTrue (Eq.refl _) => + if h : α = β then + have hp' : p'.length ≤ n := by simp [Quiver.Path.length] at hp; omega + have hq' : q'.length ≤ n := by simp [Quiver.Path.length] at hq; omega + if h'' : (⟨p', hp'⟩ : BoundedPaths _ _ n) = ⟨q', hq'⟩ then + isTrue <| by + apply Subtype.ext + dsimp + rw [h, show p' = q' from Subtype.mk.inj h''] + else + isFalse fun h => + h'' <| Subtype.ext <| eq_of_heq <| (Quiver.Path.cons.inj <| Subtype.mk.inj h).2.1 + else + isFalse fun h' => + h <| eq_of_heq (Quiver.Path.cons.inj <| Subtype.mk.inj h').2.2 + | _, _, isFalse h => isFalse fun h' => + h (Quiver.Path.cons.inj <| Subtype.mk.inj h').1 + +/-- Equality is decidable on all uniformly bounded paths given decidable +equality on the vertices and the arrows. -/ +instance decidableEqBoundedPaths [DecidableEq V] [∀ (v w : V), DecidableEq (v ⟶ w)] + (n : ℕ) : (v w : V) → DecidableEq (BoundedPaths v w n) := + n.rec decidableEqBddPathsZero + fun n decEq => decidableEqBddPathsOfDecidableEq n inferInstance inferInstance decEq + +/-- Equality is decidable on paths in a quiver given decidable equality on the vertices and +arrows. -/ +instance instDecidableEq [DecidableEq V] [∀ (v w : V), DecidableEq (v ⟶ w)] : + (v w : V) → DecidableEq (Path v w) := fun v w p q => + let m := max p.length q.length + let p' : BoundedPaths v w m := ⟨p, Nat.le_max_left ..⟩ + let q' : BoundedPaths v w m := ⟨q, Nat.le_max_right ..⟩ + decidable_of_iff (p' = q') Subtype.ext_iff + +end BoundedPath + end Path end Quiver diff --git a/Mathlib/Combinatorics/Quiver/SingleObj.lean b/Mathlib/Combinatorics/Quiver/SingleObj.lean index 2ca10de65b9aca..3d39db8e414758 100644 --- a/Mathlib/Combinatorics/Quiver/SingleObj.lean +++ b/Mathlib/Combinatorics/Quiver/SingleObj.lean @@ -75,8 +75,6 @@ arrows types. def toPrefunctor : (α → β) ≃ SingleObj α ⥤q SingleObj β where toFun f := ⟨id, f⟩ invFun f a := f.map (toHom a) - left_inv _ := rfl - right_inv _ := rfl theorem toPrefunctor_id : toPrefunctor id = 𝟭q (SingleObj α) := rfl diff --git a/Mathlib/Combinatorics/Quiver/Subquiver.lean b/Mathlib/Combinatorics/Quiver/Subquiver.lean index c200a9d3a9d2ad..0e229057a4b84f 100644 --- a/Mathlib/Combinatorics/Quiver/Subquiver.lean +++ b/Mathlib/Combinatorics/Quiver/Subquiver.lean @@ -65,8 +65,6 @@ def wideSubquiverEquivSetTotal {V} [Quiver V] : Set (Total V) where toFun H := { e | e.hom ∈ H e.left e.right } invFun S a b := { e | Total.mk a b e ∈ S } - left_inv _ := rfl - right_inv _ := rfl /-- An `L`-labelling of a quiver assigns to every arrow an element of `L`. -/ def Labelling (V : Type u) [Quiver V] (L : Sort*) := diff --git a/Mathlib/Combinatorics/SimpleGraph/CompleteMultipartite.lean b/Mathlib/Combinatorics/SimpleGraph/CompleteMultipartite.lean index 7594b002814529..2291b3f8e5ca8e 100644 --- a/Mathlib/Combinatorics/SimpleGraph/CompleteMultipartite.lean +++ b/Mathlib/Combinatorics/SimpleGraph/CompleteMultipartite.lean @@ -52,7 +52,6 @@ def IsCompleteMultipartite.iso (h : G.IsCompleteMultipartite) : G ≃g completeMultipartiteGraph (fun (c : Quotient h.setoid) ↦ {x // h.setoid.r c.out x}) where toFun := fun x ↦ ⟨_, ⟨_, Quotient.mk_out x⟩⟩ invFun := fun ⟨_, x⟩ ↦ x.1 - left_inv := fun _ ↦ rfl right_inv := fun ⟨_, x⟩ ↦ Sigma.subtype_ext (Quotient.mk_eq_iff_out.2 <| h.setoid.symm x.2) rfl map_rel_iff' := by simp_rw [Equiv.coe_fn_mk, comap_adj, top_adj, ne_eq, Quotient.eq] diff --git a/Mathlib/Combinatorics/SimpleGraph/Connectivity/WalkCounting.lean b/Mathlib/Combinatorics/SimpleGraph/Connectivity/WalkCounting.lean index 94c1111e81555a..abb894979f974d 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Connectivity/WalkCounting.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Connectivity/WalkCounting.lean @@ -70,7 +70,6 @@ def walkLengthTwoEquivCommonNeighbors (u v : V) : | ⟨.cons _ (.cons _ .nil), _⟩ => ⟨‹G.Adj u _›, ‹G.Adj _ v›.symm⟩⟩ invFun w := ⟨w.prop.1.toWalk.concat w.prop.2.symm, rfl⟩ left_inv | ⟨.cons _ (.cons _ .nil), hp⟩ => by rfl - right_inv _ := rfl section LocallyFinite diff --git a/Mathlib/Combinatorics/SimpleGraph/Copy.lean b/Mathlib/Combinatorics/SimpleGraph/Copy.lean index dd97628b246046..acadda1ca190ee 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Copy.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Copy.lean @@ -194,8 +194,6 @@ instance [Fintype {f : G →g H // Injective f}] : Fintype (G.Copy H) := .ofEquiv {f : G →g H // Injective f} { toFun f := ⟨f.1, f.2⟩ invFun f := ⟨f.1, f.2⟩ - left_inv _ := rfl - right_inv _ := rfl } end Copy diff --git a/Mathlib/Combinatorics/SimpleGraph/Dart.lean b/Mathlib/Combinatorics/SimpleGraph/Dart.lean index c36fe1186e4657..9945964687de2c 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Dart.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Dart.lean @@ -51,9 +51,7 @@ theorem Dart.toProd_injective : Function.Injective (Dart.toProd : G.Dart → V instance Dart.fintype [Fintype V] [DecidableRel G.Adj] : Fintype G.Dart := Fintype.ofEquiv (Σ v, G.neighborSet v) { toFun := fun s => ⟨(s.fst, s.snd), s.snd.property⟩ - invFun := fun d => ⟨d.fst, d.snd, d.adj⟩ - left_inv := fun s => by ext <;> simp - right_inv := fun d => by ext <;> simp } + invFun := fun d => ⟨d.fst, d.snd, d.adj⟩ } /-- The edge associated to the dart. -/ def Dart.edge (d : G.Dart) : Sym2 V := diff --git a/Mathlib/Combinatorics/SimpleGraph/Diam.lean b/Mathlib/Combinatorics/SimpleGraph/Diam.lean index 3641de07773aa7..6d0b8b4c26fa10 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Diam.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Diam.lean @@ -400,8 +400,7 @@ def center (G : SimpleGraph α) : Set α := lemma center_nonempty [Nonempty α] : G.center.Nonempty := exists_eccent_eq_radius -lemma mem_center_iff (u : α) : u ∈ G.center ↔ G.eccent u = G.radius := - Set.mem_def +lemma mem_center_iff (u : α) : u ∈ G.center ↔ G.eccent u = G.radius := .rfl lemma center_eq_univ_iff_radius_eq_ediam [Nonempty α] : G.center = Set.univ ↔ G.radius = G.ediam := by diff --git a/Mathlib/Combinatorics/SimpleGraph/Finite.lean b/Mathlib/Combinatorics/SimpleGraph/Finite.lean index feede69e73628a..844b673e49681c 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Finite.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Finite.lean @@ -496,9 +496,8 @@ theorem card_edgeFinset_induce_support : card_edgeFinset_induce_of_support_subset subset_rfl theorem map_neighborFinset_induce [DecidableEq V] (v : s) : - ((G.induce s).neighborFinset v).map (.subtype s) - = G.neighborFinset v ∩ s.toFinset := by - ext; simp [Set.mem_def] + ((G.induce s).neighborFinset v).map (.subtype (· ∈ s)) = G.neighborFinset v ∩ s.toFinset := by + ext; simp theorem map_neighborFinset_induce_of_neighborSet_subset {v : s} (h : G.neighborSet v ⊆ s) : ((G.induce s).neighborFinset v).map (.subtype s) = G.neighborFinset v := by diff --git a/Mathlib/Combinatorics/SimpleGraph/Maps.lean b/Mathlib/Combinatorics/SimpleGraph/Maps.lean index 7c0db3e670a5d3..d6ccea4de6b67a 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Maps.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Maps.lean @@ -412,8 +412,6 @@ def complEquiv : G ↪g H ≃ Gᶜ ↪g Hᶜ where obtain rfl | hvw := eq_or_ne v w · simp · simpa [hvw, not_iff_not] using f.map_adj_iff (v := v) (w := w)⟩ - left_inv f := rfl - right_inv f := rfl end Embedding diff --git a/Mathlib/Combinatorics/SimpleGraph/Matching.lean b/Mathlib/Combinatorics/SimpleGraph/Matching.lean index 7cccd3a34a7339..565ea1900b1a22 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Matching.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Matching.lean @@ -371,14 +371,16 @@ lemma IsCycles.existsUnique_ne_adj (h : G.IsCycles) (hadj : G.Adj v w) : simp_rw [← SimpleGraph.mem_neighborSet] at * aesop -lemma IsCycles.induce_supp (c : G.ConnectedComponent) (h : G.IsCycles) : - (G.induce c.supp).spanningCoe.IsCycles := by +lemma IsCycles.toSimpleGraph (c : G.ConnectedComponent) (h : G.IsCycles) : + c.toSimpleGraph.spanningCoe.IsCycles := by intro v ⟨w, hw⟩ - rw [mem_neighborSet, c.adj_spanningCoe_induce_supp] at hw + rw [mem_neighborSet, c.adj_spanningCoe_toSimpleGraph] at hw rw [← h ⟨w, hw.2⟩] congr ext w' - simp only [mem_neighborSet, c.adj_spanningCoe_induce_supp, hw, true_and] + simp only [mem_neighborSet, c.adj_spanningCoe_toSimpleGraph, hw, true_and] + +@[deprecated (since := "2025-06-08")] alias IsCycles.induce_supp := IsCycles.toSimpleGraph lemma Walk.IsCycle.isCycles_spanningCoe_toSubgraph {u : V} {p : G.Walk u u} (hpc : p.IsCycle) : p.toSubgraph.spanningCoe.IsCycles := by diff --git a/Mathlib/Combinatorics/SimpleGraph/Path.lean b/Mathlib/Combinatorics/SimpleGraph/Path.lean index b7162a73fd6733..5ed3f1fc594bf7 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Path.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Path.lean @@ -328,8 +328,7 @@ lemma IsPath.getVert_eq_start_iff {i : ℕ} {p : G.Walk u w} (hp : p.IsPath) (hi lemma IsPath.getVert_eq_end_iff {i : ℕ} {p : G.Walk u w} (hp : p.IsPath) (hi : i ≤ p.length) : p.getVert i = w ↔ i = p.length := by have := hp.reverse.getVert_eq_start_iff (by omega : p.reverse.length - i ≤ p.reverse.length) - simp only [length_reverse, getVert_reverse, - show p.length - (p.length - i) = i from by omega] at this + simp only [length_reverse, getVert_reverse, show p.length - (p.length - i) = i by omega] at this rw [this] omega @@ -893,7 +892,6 @@ lemma Reachable.mem_subgraphVerts {u v} {H : G.Subgraph} (hr : G.Reachable u v) exact aux (H.edge_vert (h _ hv' _ (Walk.adj_snd hnp)).symm) p.tail termination_by p.length decreasing_by { - simp_wf rw [← Walk.length_tail_add_one hnp] omega } @@ -1132,15 +1130,12 @@ theorem map_mk (φ : G →g G') (v : V) : rfl @[simp] -theorem map_id (C : ConnectedComponent G) : C.map Hom.id = C := by - refine C.ind ?_ - exact fun _ => rfl +theorem map_id (C : ConnectedComponent G) : C.map Hom.id = C := C.ind (fun _ => rfl) @[simp] theorem map_comp (C : G.ConnectedComponent) (φ : G →g G') (ψ : G' →g G'') : - (C.map φ).map ψ = C.map (ψ.comp φ) := by - refine C.ind ?_ - exact fun _ => rfl + (C.map φ).map ψ = C.map (ψ.comp φ) := + C.ind (fun _ => rfl) variable {φ : G ≃g G'} {v : V} {v' : V'} @@ -1167,10 +1162,8 @@ namespace Iso def connectedComponentEquiv (φ : G ≃g G') : G.ConnectedComponent ≃ G'.ConnectedComponent where toFun := ConnectedComponent.map φ invFun := ConnectedComponent.map φ.symm - left_inv C := ConnectedComponent.ind - (fun v => congr_arg G.connectedComponentMk (Equiv.left_inv φ.toEquiv v)) C - right_inv C := ConnectedComponent.ind - (fun v => congr_arg G'.connectedComponentMk (Equiv.right_inv φ.toEquiv v)) C + left_inv C := C.ind (fun v => congr_arg G.connectedComponentMk (Equiv.left_inv φ.toEquiv v)) + right_inv C := C.ind (fun v => congr_arg G'.connectedComponentMk (Equiv.right_inv φ.toEquiv v)) @[simp] theorem connectedComponentEquiv_refl : @@ -1203,9 +1196,8 @@ def supp (C : G.ConnectedComponent) := theorem supp_injective : Function.Injective (ConnectedComponent.supp : G.ConnectedComponent → Set V) := by refine ConnectedComponent.ind₂ ?_ - intro v w simp only [ConnectedComponent.supp, Set.ext_iff, ConnectedComponent.eq, Set.mem_setOf_eq] - intro h + intro v w h rw [reachable_comm, h] @[simp] @@ -1228,15 +1220,6 @@ lemma mem_supp_congr_adj {v w : V} (c : G.ConnectedComponent) (hadj : G.Adj v w) · exact hadj.symm · exact hadj -lemma adj_spanningCoe_induce_supp {v w : V} (c : G.ConnectedComponent) : - (G.induce c.supp).spanningCoe.Adj v w ↔ v ∈ c.supp ∧ G.Adj v w := by - by_cases h : v ∈ c.supp - · refine ⟨by aesop, ?_⟩ - intro h' - have : w ∈ c.supp := by rwa [c.mem_supp_congr_adj h'.2] at h - aesop - · aesop - theorem connectedComponentMk_mem {v : V} : v ∈ G.connectedComponentMk v := rfl @@ -1263,8 +1246,7 @@ lemma mem_coe_supp_of_adj {v w : V} {H : Subgraph G} {c : ConnectedComponent H.c lemma eq_of_common_vertex {v : V} {c c' : ConnectedComponent G} (hc : v ∈ c.supp) (hc' : v ∈ c'.supp) : c = c' := by simp only [mem_supp_iff] at * - subst hc hc' - rfl + rw [← hc, ← hc'] lemma connectedComponentMk_supp_subset_supp {G'} {v : V} (h : G ≤ G') (c' : G'.ConnectedComponent) (hc' : v ∈ c'.supp) : (G.connectedComponentMk v).supp ⊆ c'.supp := by @@ -1288,28 +1270,84 @@ lemma top_supp_eq_univ (c : ConnectedComponent (⊤ : SimpleGraph V)) : have ⟨w, hw⟩ := c.exists_rep ext v simp only [Set.mem_univ, iff_true, mem_supp_iff, ← hw] - apply SimpleGraph.ConnectedComponent.sound - exact (@SimpleGraph.top_connected V (Nonempty.intro v)).preconnected v w + apply ConnectedComponent.sound + exact (@top_connected V (Nonempty.intro v)).preconnected v w + +lemma reachable_of_mem_supp {G : SimpleGraph V} (C : G.ConnectedComponent) {u v : V} + (hu : u ∈ C.supp) (hv : v ∈ C.supp) : G.Reachable u v := by + rw [mem_supp_iff] at hu hv + exact ConnectedComponent.exact (hv ▸ hu) + +lemma mem_supp_of_adj_mem_supp {G : SimpleGraph V} (C : G.ConnectedComponent) {u v : V} + (hu : u ∈ C.supp) (hadj : G.Adj u v) : v ∈ C.supp := by + have hC : G.connectedComponentMk u = G.connectedComponentMk v := + connectedComponentMk_eq_of_adj hadj + rw [hu] at hC + exact hC.symm + +/-- +Given a connected component `C` of a simple graph `G`, produce the induced graph on `C`. +The declaration `connected_toSimpleGraph` shows it is connected, and `toSimpleGraph_hom` +provides the homomorphism back to `G`. +-/ +def toSimpleGraph {G : SimpleGraph V} (C : G.ConnectedComponent) : SimpleGraph C := G.induce C.supp -lemma reachable_induce_supp {v w} {c : ConnectedComponent G} (hv : v ∈ c.supp) (hw : w ∈ c.supp) - (p : G.Walk v w) : (G.induce c.supp).Reachable ⟨v, hv⟩ ⟨w, hw⟩ := by - induction p with - | nil => rfl - | @cons u v w h p ih => - have : v ∈ c.supp := (c.mem_supp_congr_adj h).mp hv - obtain ⟨q⟩ := ih this hw - have hadj : (G.induce c.supp).Adj ⟨u, hv⟩ ⟨v, this⟩ := h - use q.cons hadj - -lemma connected_induce_supp (c : ConnectedComponent G) : (G.induce c.supp).Connected := by - rw [connected_iff_exists_forall_reachable] - use ⟨c.out, c.out_eq⟩ - intro w - have hwc := (c.mem_supp_iff w).mp (Subtype.coe_prop w) - obtain ⟨p⟩ := ConnectedComponent.exact - (show G.connectedComponentMk c.out = G.connectedComponentMk w by - simp [← hwc, connectedComponentMk]) - exact c.reachable_induce_supp c.out_eq hwc p +/-- Homomorphism from a connected component graph to the original graph. -/ +def toSimpleGraph_hom {G : SimpleGraph V} (C : G.ConnectedComponent) : C.toSimpleGraph →g G where + toFun u := u.val + map_rel' := id + +lemma toSimpleGraph_hom_apply {G : SimpleGraph V} (C : G.ConnectedComponent) (u : C) : + C.toSimpleGraph_hom u = u.val := rfl + +lemma toSimpleGraph_adj {G : SimpleGraph V} (C : G.ConnectedComponent) {u v : V} (hu : u ∈ C) + (hv : v ∈ C) : C.toSimpleGraph.Adj ⟨u, hu⟩ ⟨v, hv⟩ ↔ G.Adj u v := by + simp [toSimpleGraph] + +lemma adj_spanningCoe_toSimpleGraph {v w : V} (C : G.ConnectedComponent) : + C.toSimpleGraph.spanningCoe.Adj v w ↔ v ∈ C.supp ∧ G.Adj v w := by + apply Iff.intro + · intro h + simp_all only [map_adj, SetLike.coe_sort_coe, Subtype.exists, mem_supp_iff] + obtain ⟨_, a, _, _, h₁, h₂, h₃⟩ := h + subst h₂ h₃ + exact ⟨a, h₁⟩ + · simp only [toSimpleGraph, map_adj, comap_adj, Embedding.subtype_apply, Subtype.exists, + exists_and_left, and_imp] + intro h hadj + exact ⟨v, h, w, hadj, rfl, (C.mem_supp_congr_adj hadj).mp h, rfl⟩ + +@[deprecated (since := "2025-05-08")] +alias adj_spanningCoe_induce_supp := adj_spanningCoe_toSimpleGraph + +/-- Get the walk between two vertices in a connected component from a walk in the original graph. -/ +private def walk_toSimpleGraph' {G : SimpleGraph V} (C : G.ConnectedComponent) {u v : V} + (hu : u ∈ C) (hv : v ∈ C) (p : G.Walk u v) : C.toSimpleGraph.Walk ⟨u, hu⟩ ⟨v, hv⟩ := by + cases p with + | nil => exact Walk.nil + | @cons v w u h p => + have hw : w ∈ C := C.mem_supp_of_adj_mem_supp hu h + have h' : C.toSimpleGraph.Adj ⟨u, hu⟩ ⟨w, hw⟩ := h + exact Walk.cons h' (C.walk_toSimpleGraph' hw hv p) + +@[deprecated (since := "2025-05-08")] alias reachable_induce_supp := walk_toSimpleGraph' + +/-- There is a walk between every pair of vertices in a connected component. -/ +noncomputable def walk_toSimpleGraph {G : SimpleGraph V} (C : G.ConnectedComponent) {u v : V} + (hu : u ∈ C) (hv : v ∈ C) : C.toSimpleGraph.Walk ⟨u, hu⟩ ⟨v, hv⟩ := + C.walk_toSimpleGraph' hu hv (C.reachable_of_mem_supp hu hv).some + +lemma reachable_toSimpleGraph {G : SimpleGraph V} (C : G.ConnectedComponent) {u v : V} + (hu : u ∈ C) (hv : v ∈ C) : C.toSimpleGraph.Reachable ⟨u, hu⟩ ⟨v, hv⟩ := + Walk.reachable (C.walk_toSimpleGraph hu hv) + +lemma connected_toSimpleGraph (C : ConnectedComponent G) : (C.toSimpleGraph).Connected where + preconnected := by + intro ⟨u, hu⟩ ⟨v, hv⟩ + exact C.reachable_toSimpleGraph hu hv + nonempty := ⟨C.out, C.out_eq⟩ + +@[deprecated (since := "2025-05-08")] alias connected_induce_supp := connected_toSimpleGraph end ConnectedComponent @@ -1327,7 +1365,7 @@ lemma iUnion_connectedComponentSupp (G : SimpleGraph V) : ⋃ c : G.ConnectedComponent, c.supp = Set.univ := by refine Set.eq_univ_of_forall fun v ↦ ⟨G.connectedComponentMk v, ?_⟩ simp only [Set.mem_range, SetLike.mem_coe] - exact ⟨by use G.connectedComponentMk v; exact rfl, rfl⟩ + exact ⟨⟨G.connectedComponentMk v, rfl⟩, rfl⟩ theorem Preconnected.set_univ_walk_nonempty (hconn : G.Preconnected) (u v : V) : (Set.univ : Set (G.Walk u v)).Nonempty := by @@ -1361,8 +1399,7 @@ theorem reachable_delete_edges_iff_exists_walk {v w v' w' : V} : simpa using p.edges_subset_edgeSet h · rintro ⟨p, h⟩ refine ⟨p.transfer _ fun e ep => ?_⟩ - simp only [edgeSet_sdiff, edgeSet_fromEdgeSet, edgeSet_sdiff_sdiff_isDiag, Set.mem_diff, - Set.mem_singleton_iff] + simp only [edgeSet_sdiff, edgeSet_fromEdgeSet, edgeSet_sdiff_sdiff_isDiag] exact ⟨p.edges_subset_edgeSet ep, fun h' => h (h' ▸ ep)⟩ theorem isBridge_iff_adj_and_forall_walk_mem_edges {v w : V} : diff --git a/Mathlib/Combinatorics/SimpleGraph/Subgraph.lean b/Mathlib/Combinatorics/SimpleGraph/Subgraph.lean index 2987420c759b10..59aa4e3ff75b54 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Subgraph.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Subgraph.lean @@ -190,8 +190,6 @@ def spanningCoeEquivCoeOfSpanning (G' : Subgraph G) (h : G'.IsSpanning) : G'.spanningCoe ≃g G'.coe where toFun v := ⟨v, h v⟩ invFun v := v - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := Iff.rfl /-- A subgraph is called an *induced subgraph* if vertices of `G'` are adjacent if @@ -228,8 +226,6 @@ def coeNeighborSetEquiv {G' : Subgraph G} (v : G'.verts) : G'.coe.neighborSet v ≃ G'.neighborSet v where toFun w := ⟨w, w.2⟩ invFun w := ⟨⟨w, G'.edge_vert (G'.adj_symm w.2)⟩, w.2⟩ - left_inv _ := rfl - right_inv _ := rfl /-- The edge set of `G'` consists of a subset of edges of `G`. -/ def edgeSet (G' : Subgraph G) : Set (Sym2 V) := Sym2.fromRel G'.symm @@ -425,7 +421,6 @@ def topIso : (⊤ : G.Subgraph).coe ≃g G where toFun := (↑) invFun a := ⟨a, Set.mem_univ _⟩ left_inv _ := Subtype.eta .. - right_inv _ := rfl map_rel_iff' := .rfl theorem verts_spanningCoe_injective : @@ -586,8 +581,6 @@ lemma sup_spanningCoe (H H' : Subgraph G) : def topEquiv : (⊤ : Subgraph G).coe ≃g G where toFun v := ↑v invFun v := ⟨v, trivial⟩ - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := Iff.rfl /-- The bottom of the `Subgraph G` lattice is equivalent to the empty graph on the empty diff --git a/Mathlib/Combinatorics/SimpleGraph/Sum.lean b/Mathlib/Combinatorics/SimpleGraph/Sum.lean index f50433c8192862..3484154eb3c1f0 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Sum.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Sum.lean @@ -96,7 +96,6 @@ def Coloring.sumEquiv : (G ⊕g H).Coloring γ ≃ G.Coloring γ × H.Coloring toFun c := ⟨c.sumLeft, c.sumRight⟩ invFun p := p.1.sum p.2 left_inv c := by simp [sum_sumLeft_sumRight c] - right_inv p := rfl /-- Color `G ⊕g H` with `Fin (n + m)` given a coloring of `G` with `Fin n` and a coloring of `H` with `Fin m` -/ diff --git a/Mathlib/Combinatorics/SimpleGraph/Tutte.lean b/Mathlib/Combinatorics/SimpleGraph/Tutte.lean index d09d407176ff47..320ea6f56c785e 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Tutte.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Tutte.lean @@ -187,21 +187,22 @@ private theorem tutte_exists_isPerfectMatching_of_near_matchings {x a b c : V} have hM1sub := Subgraph.spanningCoe_le M1 have hM2sub := Subgraph.spanningCoe_le M2 -- We consider the cycle that contains the vertex `c` - have induce_le : (induce (cycles.connectedComponentMk c).supp cycles).spanningCoe ≤ + have induce_le : ((cycles.connectedComponentMk c).toSimpleGraph).spanningCoe ≤ (G ⊔ edge a c) ⊔ edge x b := by refine le_trans (spanningCoe_induce_le cycles (cycles.connectedComponentMk c).supp) ?_ simp only [← hsupG, cycles] exact le_trans (by apply symmDiff_le_sup) (sup_le_sup hM1sub hM2sub) -- If that cycle does not contain the vertex `x`, we use it as an alternating cycle by_cases hxc : x ∉ (cycles.connectedComponentMk c).supp - · use (cycles.induce (cycles.connectedComponentMk c).supp).spanningCoe + · use (cycles.connectedComponentMk c).toSimpleGraph.spanningCoe refine ⟨hcalt.mono (spanningCoe_induce_le cycles (cycles.connectedComponentMk c).supp), ?_⟩ - simp only [ConnectedComponent.adj_spanningCoe_induce_supp, hxc, hcac, false_and, + simp only [ConnectedComponent.adj_spanningCoe_toSimpleGraph, hxc, hcac, false_and, not_false_eq_true, ConnectedComponent.mem_supp_iff, ConnectedComponent.eq, and_true, true_and, hcac.reachable] - refine ⟨hcycles.induce_supp (cycles.connectedComponentMk c), + refine ⟨hcycles.toSimpleGraph (cycles.connectedComponentMk c), Disjoint.left_le_of_le_sup_right induce_le ?_⟩ rw [disjoint_edge] + rw [ConnectedComponent.adj_spanningCoe_toSimpleGraph] aesop push_neg at hxc have hacc := ((cycles.connectedComponentMk c).mem_supp_congr_adj hcac.symm).mp rfl @@ -289,12 +290,13 @@ lemma exists_isTutteViolator (h : ∀ (M : G.Subgraph), ¬M.IsPerfectMatching) push_neg at h' obtain ⟨K, hK⟩ := h' obtain ⟨x, y, hxy⟩ := (not_isClique_iff _).mp hK - obtain ⟨p , hp⟩ := Reachable.exists_path_of_dist (K.connected_induce_supp x y) + obtain ⟨p , hp⟩ := Reachable.exists_path_of_dist (K.connected_toSimpleGraph x y) obtain ⟨x, a, b, hxa, hxb, hnadjxb, hnxb⟩ := Walk.exists_adj_adj_not_adj_ne hp.2 (p.reachable.one_lt_dist_of_ne_of_not_adj hxy.1 hxy.2) - simp only [deleteUniversalVerts, universalVerts, ne_eq, Subgraph.induce_verts, - Subgraph.verts_top, comap_adj, Function.Embedding.coe_subtype, Subgraph.coe_adj, - Subgraph.induce_adj, Subtype.coe_prop, Subgraph.top_adj, true_and] at hxa hxb hnadjxb + simp only [ConnectedComponent.toSimpleGraph, deleteUniversalVerts, universalVerts, ne_eq, + Subgraph.induce_verts, Subgraph.verts_top, comap_adj, Function.Embedding.coe_subtype, + Subgraph.coe_adj, Subgraph.induce_adj, Subtype.coe_prop, Subgraph.top_adj, true_and + ] at hxa hxb hnadjxb obtain ⟨c, hc⟩ : ∃ (c : V), (a : V) ≠ c ∧ ¬ Gmax.Adj c a := by simpa [universalVerts] using a.1.2.2 have hbnec : b.val.val ≠ c := by rintro rfl; exact hc.2 hxb.symm diff --git a/Mathlib/Condensed/Light/TopCatAdjunction.lean b/Mathlib/Condensed/Light/TopCatAdjunction.lean index 27ce1c170de994..b7080a84ca14c1 100644 --- a/Mathlib/Condensed/Light/TopCatAdjunction.lean +++ b/Mathlib/Condensed/Light/TopCatAdjunction.lean @@ -86,8 +86,6 @@ but not an isomorphism in general (the inverse isn't continuous unless `X` is se noncomputable def topCatAdjunctionCounitEquiv (X : TopCat.{u}) : X.toLightCondSet.toTopCat ≃ X where toFun := topCatAdjunctionCounit X invFun x := ContinuousMap.const _ x - left_inv _ := rfl - right_inv _ := rfl lemma topCatAdjunctionCounit_bijective (X : TopCat.{u}) : Function.Bijective (topCatAdjunctionCounit X) := diff --git a/Mathlib/Condensed/TopCatAdjunction.lean b/Mathlib/Condensed/TopCatAdjunction.lean index 2bfb89ffd408d3..bfd7b2a2e8499e 100644 --- a/Mathlib/Condensed/TopCatAdjunction.lean +++ b/Mathlib/Condensed/TopCatAdjunction.lean @@ -97,8 +97,6 @@ noncomputable def topCatAdjunctionCounitEquiv (X : TopCat.{u + 1}) : X.toCondensedSet.toTopCat ≃ X where toFun := topCatAdjunctionCounit X invFun x := ContinuousMap.const _ x - left_inv _ := rfl - right_inv _ := rfl lemma topCatAdjunctionCounit_bijective (X : TopCat.{u + 1}) : Function.Bijective (topCatAdjunctionCounit X) := diff --git a/Mathlib/Control/Functor.lean b/Mathlib/Control/Functor.lean index cce2a0cada8538..d8302c4b96e0a7 100644 --- a/Mathlib/Control/Functor.lean +++ b/Mathlib/Control/Functor.lean @@ -234,7 +234,7 @@ instance instApplicativeComp : Applicative (Comp F G) := end Comp -variable {F : Type u → Type u} [Functor F] +variable {F : Type u → Type v} [Functor F] /-- If we consider `x : F α` to, in some sense, contain values of type `α`, predicate `Liftp p x` holds iff every value contained by `x` satisfies `p`. -/ diff --git a/Mathlib/Data/BitVec.lean b/Mathlib/Data/BitVec.lean index 97b978bb62dfde..857b146d9783a9 100644 --- a/Mathlib/Data/BitVec.lean +++ b/Mathlib/Data/BitVec.lean @@ -119,8 +119,6 @@ instance : CommRing (BitVec w) := def equivFin {m : ℕ} : BitVec m ≃+* Fin (2 ^ m) where toFun a := a.toFin invFun a := ofFin a - left_inv _ := rfl - right_inv _ := rfl map_mul' _ _ := rfl map_add' _ _ := rfl diff --git a/Mathlib/Data/Bundle.lean b/Mathlib/Data/Bundle.lean index f45b4faa76f851..9e261b92c7d654 100644 --- a/Mathlib/Data/Bundle.lean +++ b/Mathlib/Data/Bundle.lean @@ -108,8 +108,6 @@ def TotalSpace.trivialSnd (B : Type*) (F : Type*) : TotalSpace F (Bundle.Trivial def TotalSpace.toProd (B F : Type*) : (TotalSpace F fun _ : B => F) ≃ B × F where toFun x := (x.1, x.2) invFun x := ⟨x.1, x.2⟩ - left_inv := fun ⟨_, _⟩ => rfl - right_inv := fun ⟨_, _⟩ => rfl section Pullback diff --git a/Mathlib/Data/Complex/Basic.lean b/Mathlib/Data/Complex/Basic.lean index 0d76c7c9aeef36..4fd841fdc1bf04 100644 --- a/Mathlib/Data/Complex/Basic.lean +++ b/Mathlib/Data/Complex/Basic.lean @@ -45,8 +45,6 @@ noncomputable instance : DecidableEq ℂ := def equivRealProd : ℂ ≃ ℝ × ℝ where toFun z := ⟨z.re, z.im⟩ invFun p := ⟨p.1, p.2⟩ - left_inv := fun ⟨_, _⟩ => rfl - right_inv := fun ⟨_, _⟩ => rfl @[simp] theorem eta : ∀ z : ℂ, Complex.mk z.re z.im = z diff --git a/Mathlib/Data/Complex/Module.lean b/Mathlib/Data/Complex/Module.lean index 43df3d73ebec04..df60f3beebbfa2 100644 --- a/Mathlib/Data/Complex/Module.lean +++ b/Mathlib/Data/Complex/Module.lean @@ -475,7 +475,6 @@ def Complex.selfAdjointEquiv : selfAdjoint ℂ ≃ₗ[ℝ] ℝ where toFun := fun z ↦ (z : ℂ).re invFun := fun x ↦ ⟨x, conj_ofReal x⟩ left_inv := fun z ↦ Subtype.ext <| conj_eq_iff_re.mp z.property.star_eq - right_inv := fun _ ↦ rfl map_add' := by simp map_smul' := by simp diff --git a/Mathlib/Data/DFinsupp/Defs.lean b/Mathlib/Data/DFinsupp/Defs.lean index b81a905f02fe9b..4fe83507d4d8e7 100644 --- a/Mathlib/Data/DFinsupp/Defs.lean +++ b/Mathlib/Data/DFinsupp/Defs.lean @@ -440,7 +440,6 @@ def equivFunOnFintype [Fintype ι] : (Π₀ i, β i) ≃ ∀ i, β i where toFun := (⇑) invFun f := ⟨f, Trunc.mk ⟨Finset.univ.1, fun _ => Or.inl <| Finset.mem_univ_val _⟩⟩ left_inv _ := DFunLike.coe_injective rfl - right_inv _ := rfl @[simp] theorem equivFunOnFintype_symm_coe [Fintype ι] (f : Π₀ i, β i) : equivFunOnFintype.symm f = f := diff --git a/Mathlib/Data/Fin/Basic.lean b/Mathlib/Data/Fin/Basic.lean index 53ec2311959cb6..87f401d9975bbb 100644 --- a/Mathlib/Data/Fin/Basic.lean +++ b/Mathlib/Data/Fin/Basic.lean @@ -96,8 +96,6 @@ lemma ne_last_of_lt {a b : Fin (n + 1)} (hab : a < b) : a ≠ last n := def equivSubtype : Fin n ≃ { i // i < n } where toFun a := ⟨a.1, a.2⟩ invFun a := ⟨a.1, a.2⟩ - left_inv := fun ⟨_, _⟩ => rfl - right_inv := fun ⟨_, _⟩ => rfl section coe @@ -310,6 +308,21 @@ theorem nontrivial_iff_two_le : Nontrivial (Fin n) ↔ 2 ≤ n := by rcases n with (_ | _ | n) <;> simp [Fin.nontrivial, not_nontrivial, Nat.succ_le_iff] +/-- If working with more than two elements, we can always pick a third distinct from two existing +elements. -/ +theorem exists_ne_and_ne_of_two_lt (i j : Fin n) (h : 2 < n) : ∃ k, k ≠ i ∧ k ≠ j := by + have : NeZero n := ⟨by omega⟩ + rcases i with ⟨i, hi⟩ + rcases j with ⟨j, hj⟩ + simp_rw [← Fin.val_ne_iff] + by_cases h0 : 0 ≠ i ∧ 0 ≠ j + · exact ⟨0, h0⟩ + · by_cases h1 : 1 ≠ i ∧ 1 ≠ j + · exact ⟨⟨1, by omega⟩, h1⟩ + · refine ⟨⟨2, by omega⟩, ?_⟩ + dsimp only + omega + section Monoid instance inhabitedFinOneAdd (n : ℕ) : Inhabited (Fin (1 + n)) := @@ -407,6 +420,14 @@ lemma natCast_mono (hbn : b ≤ n) (hab : a ≤ b) : (a : Fin (n + 1)) ≤ b := lemma natCast_strictMono (hbn : b ≤ n) (hab : a < b) : (a : Fin (n + 1)) < b := (natCast_lt_natCast (hab.le.trans hbn) hbn).2 hab +@[simp] +lemma castLE_natCast {m n : ℕ} [NeZero m] (h : m ≤ n) (a : ℕ) : + letI : NeZero n := ⟨Nat.pos_iff_ne_zero.mp (lt_of_lt_of_le m.pos_of_neZero h)⟩ + Fin.castLE h (a.cast : Fin m) = (a % m : ℕ) := by + ext + simp only [coe_castLE, val_natCast] + rw [Nat.mod_eq_of_lt (a := a % m) (lt_of_lt_of_le (Nat.mod_lt _ m.pos_of_neZero) h)] + end OfNatCoe end Add diff --git a/Mathlib/Data/Fin/Tuple/Basic.lean b/Mathlib/Data/Fin/Tuple/Basic.lean index 7abefa2185c391..9697040bd0eab5 100644 --- a/Mathlib/Data/Fin/Tuple/Basic.lean +++ b/Mathlib/Data/Fin/Tuple/Basic.lean @@ -1215,4 +1215,3 @@ def piFinTwoEquiv (α : Fin 2 → Type u) : (∀ i, α i) ≃ α 0 × α 1 where toFun f := (f 0, f 1) invFun p := Fin.cons p.1 <| Fin.cons p.2 finZeroElim left_inv _ := funext <| Fin.forall_fin_two.2 ⟨rfl, rfl⟩ - right_inv := fun _ => rfl diff --git a/Mathlib/Data/Fin/Tuple/Embedding.lean b/Mathlib/Data/Fin/Tuple/Embedding.lean index 22511c97b24b2f..d5b93c86e8f7b3 100644 --- a/Mathlib/Data/Fin/Tuple/Embedding.lean +++ b/Mathlib/Data/Fin/Tuple/Embedding.lean @@ -113,7 +113,6 @@ def twoEmbeddingEquiv : (Fin 2 ↪ α) ≃ { (a, b) : α × α | a ≠ b } where by_cases hi : i = 0 · rw [if_pos hi, hi] · rw [if_neg hi, Fin.eq_one_of_ne_zero i hi] - right_inv := fun ⟨⟨a, b⟩, h⟩ ↦ by simp /-- Two distinct elements of `α` give an embedding `Fin 2 ↪ α`. -/ def embFinTwo {a b : α} (h : a ≠ b) : Fin 2 ↪ α := diff --git a/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean b/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean index 4175f8a6939ce3..7cb0e0b0276dfd 100644 --- a/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean +++ b/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean @@ -240,7 +240,6 @@ def sigmaAntidiagonalTupleEquivTuple (k : ℕ) : (Σ n, antidiagonalTuple k n) toFun x := x.2 invFun x := ⟨∑ i, x i, x, mem_antidiagonalTuple.mpr rfl⟩ left_inv := fun ⟨_, _, h⟩ => Sigma.subtype_ext (mem_antidiagonalTuple.mp h) rfl - right_inv _ := rfl end EquivProd diff --git a/Mathlib/Data/Finset/Basic.lean b/Mathlib/Data/Finset/Basic.lean index a2a49b951c14e9..cc9e4c7944a5c2 100644 --- a/Mathlib/Data/Finset/Basic.lean +++ b/Mathlib/Data/Finset/Basic.lean @@ -373,10 +373,13 @@ theorem disjoint_filter_filter_neg (s t : Finset α) (p : α → Prop) Disjoint (s.filter p) (t.filter fun a => ¬p a) := disjoint_filter_filter' s t disjoint_compl_right -theorem filter_disj_union (s : Finset α) (t : Finset α) (h : Disjoint s t) : +theorem filter_disjUnion (s : Finset α) (t : Finset α) (h : Disjoint s t) : filter p (disjUnion s t h) = (filter p s).disjUnion (filter p t) (disjoint_filter_filter h) := eq_of_veq <| Multiset.filter_add _ _ _ +@[deprecated (since := "2025-06-11")] +alias filter_disj_union := filter_disjUnion + theorem filter_cons {a : α} (s : Finset α) (ha : a ∉ s) : filter p (cons a s ha) = if p a then cons a (filter p s) ((mem_of_mem_filter _).mt ha) else filter p s := by @@ -659,8 +662,6 @@ def piFinsetUnion {ι} [DecidableEq ι] (α : ι → Type*) {s t : Finset ι} (h def _root_.Finset.equivToSet (s : Finset α) : s ≃ s.toSet where toFun a := ⟨a.1, mem_coe.2 a.2⟩ invFun a := ⟨a.1, mem_coe.1 a.2⟩ - left_inv := fun _ ↦ rfl - right_inv := fun _ ↦ rfl end Equiv diff --git a/Mathlib/Data/Finset/Density.lean b/Mathlib/Data/Finset/Density.lean index 45a1e609badfb8..4519f836e3203f 100644 --- a/Mathlib/Data/Finset/Density.lean +++ b/Mathlib/Data/Finset/Density.lean @@ -100,7 +100,6 @@ lemma dens_map_le [Fintype β] (f : α ↪ β) : dens (s.map f) ≤ dens s := by · simp [Subsingleton.elim s ∅] simp_rw [dens, card_map] gcongr - · positivity · exact mod_cast Fintype.card_pos · exact Fintype.card_le_of_injective _ f.2 diff --git a/Mathlib/Data/Finset/Empty.lean b/Mathlib/Data/Finset/Empty.lean index e7b2249406b8db..4adcf195bd8aa2 100644 --- a/Mathlib/Data/Finset/Empty.lean +++ b/Mathlib/Data/Finset/Empty.lean @@ -59,7 +59,7 @@ alias ⟨_, Nonempty.coe_sort⟩ := nonempty_coe_sort theorem Nonempty.exists_mem {s : Finset α} (h : s.Nonempty) : ∃ x : α, x ∈ s := h -theorem Nonempty.mono {s t : Finset α} (hst : s ⊆ t) (hs : s.Nonempty) : t.Nonempty := +@[gcongr] theorem Nonempty.mono {s t : Finset α} (hst : s ⊆ t) (hs : s.Nonempty) : t.Nonempty := Set.Nonempty.mono hst hs theorem Nonempty.forall_const {s : Finset α} (h : s.Nonempty) {p : Prop} : (∀ x ∈ s, p) ↔ p := diff --git a/Mathlib/Data/Finset/NatAntidiagonal.lean b/Mathlib/Data/Finset/NatAntidiagonal.lean index 7718c551501cc4..8282411550b744 100644 --- a/Mathlib/Data/Finset/NatAntidiagonal.lean +++ b/Mathlib/Data/Finset/NatAntidiagonal.lean @@ -154,8 +154,6 @@ def antidiagonalEquivFin (n : ℕ) : antidiagonal n ≃ Fin (n + 1) where invFun := fun ⟨i, h⟩ ↦ ⟨⟨i, n - i⟩, by rw [mem_antidiagonal, add_comm, Nat.sub_add_cancel] exact Nat.le_of_lt_succ h⟩ - left_inv := by rintro ⟨⟨i, j⟩, h⟩; ext; rfl - right_inv _ := rfl end Nat diff --git a/Mathlib/Data/Finset/Sum.lean b/Mathlib/Data/Finset/Sum.lean index 0247bddc700b9c..7a461441b2613e 100644 --- a/Mathlib/Data/Finset/Sum.lean +++ b/Mathlib/Data/Finset/Sum.lean @@ -93,10 +93,13 @@ theorem disjSum_ssubset_disjSum_of_subset_of_ssubset (hs : s₁ ⊆ s₂) (ht : theorem disjSum_strictMono_left (t : Finset β) : StrictMono fun s : Finset α => s.disjSum t := fun _ _ hs => disjSum_ssubset_disjSum_of_ssubset_of_subset hs Subset.rfl -theorem disj_sum_strictMono_right (s : Finset α) : +theorem disjSum_strictMono_right (s : Finset α) : StrictMono (s.disjSum : Finset β → Finset (α ⊕ β)) := fun _ _ => disjSum_ssubset_disjSum_of_subset_of_ssubset Subset.rfl +@[deprecated (since := "2025-06-11")] +alias disj_sum_strictMono_right := disjSum_strictMono_right + @[simp] lemma disjSum_inj {α β : Type*} {s₁ s₂ : Finset α} {t₁ t₂ : Finset β} : s₁.disjSum t₁ = s₂.disjSum t₂ ↔ s₁ = s₂ ∧ t₁ = t₂ := by simp [Finset.ext_iff] diff --git a/Mathlib/Data/Finset/SymmDiff.lean b/Mathlib/Data/Finset/SymmDiff.lean index 9b38f1059010e1..977f6584f83616 100644 --- a/Mathlib/Data/Finset/SymmDiff.lean +++ b/Mathlib/Data/Finset/SymmDiff.lean @@ -52,6 +52,18 @@ theorem image_symmDiff [DecidableEq β] {f : α → β} (s t : Finset α) (hf : (s ∆ t).image f = s.image f ∆ t.image f := mod_cast Set.image_symmDiff hf s t +/-- See `symmDiff_subset_sdiff'` for the swapped version of this. -/ +lemma symmDiff_subset_sdiff : s \ t ⊆ s ∆ t := subset_union_left + +/-- See `symmDiff_subset_sdiff` for the swapped version of this. -/ +lemma symmDiff_subset_sdiff' : t \ s ⊆ s ∆ t := subset_union_right + +lemma symmDiff_subset_union : s ∆ t ⊆ s ∪ t := symmDiff_le_sup (α := Finset α) + +lemma symmDiff_eq_union_iff (s t : Finset α) : s ∆ t = s ∪ t ↔ Disjoint s t := symmDiff_eq_sup s t + +lemma symmDiff_eq_union (h : Disjoint s t) : s ∆ t = s ∪ t := Disjoint.symmDiff_eq_sup h + end SymmDiff end Finset diff --git a/Mathlib/Data/Finsupp/Basic.lean b/Mathlib/Data/Finsupp/Basic.lean index cb22c7bb0aa43b..0b970117c03157 100644 --- a/Mathlib/Data/Finsupp/Basic.lean +++ b/Mathlib/Data/Finsupp/Basic.lean @@ -754,6 +754,26 @@ theorem some_single_some [Zero M] (a : α) (m : M) : ext b simp [single_apply] +@[simp] +theorem embDomain_some_some [Zero M] (f : α →₀ M) (x) : f.embDomain .some (.some x) = f x := by + simp [← Function.Embedding.some_apply] + +@[simp] +theorem some_update_none [Zero M] (f : Option α →₀ M) (a : M) : + (f.update .none a).some = f.some := by + ext + simp [Finsupp.update] + +/-- `Finsupp`s from `Option` are equivalent to +pairs of an element and a `Finsupp` on the original type. -/ +@[simps] +noncomputable +def optionEquiv [Zero M] : (Option α →₀ M) ≃ M × (α →₀ M) where + toFun P := (P .none, P.some) + invFun P := (P.2.embDomain .some).update .none P.1 + left_inv P := by ext (_|a) <;> simp [Finsupp.update] + right_inv P := by ext <;> simp [Finsupp.update] + @[to_additive] theorem prod_option_index [AddZeroClass M] [CommMonoid N] (f : Option α →₀ M) (b : Option α → M → N) (h_zero : ∀ o, b o 0 = 1) @@ -1194,7 +1214,7 @@ theorem sumElim_inr {α β γ : Type*} [Zero γ] (f : α →₀ γ) (g : β → lemma prod_sumElim {ι₁ ι₂ α M : Type*} [Zero α] [CommMonoid M] (f₁ : ι₁ →₀ α) (f₂ : ι₂ →₀ α) (g : ι₁ ⊕ ι₂ → α → M) : (f₁.sumElim f₂).prod g = f₁.prod (g ∘ Sum.inl) * f₂.prod (g ∘ Sum.inr) := by - simp [Finsupp.prod, Finset.prod_disj_sum] + simp [Finsupp.prod, Finset.prod_disjSum] /-- The equivalence between `(α ⊕ β) →₀ γ` and `(α →₀ γ) × (β →₀ γ)`. @@ -1491,3 +1511,5 @@ theorem sigmaFinsuppAddEquivPiFinsupp_apply {α : Type*} {ιs : η → Type*} [A end Sigma end Finsupp + +set_option linter.style.longFile 1700 diff --git a/Mathlib/Data/Finsupp/Defs.lean b/Mathlib/Data/Finsupp/Defs.lean index 9e094fe9d69fd2..bb3984b403c5b3 100644 --- a/Mathlib/Data/Finsupp/Defs.lean +++ b/Mathlib/Data/Finsupp/Defs.lean @@ -187,8 +187,6 @@ theorem support_subset_iff {s : Set α} {f : α →₀ M} : def equivFunOnFinite [Finite α] : (α →₀ M) ≃ (α → M) where toFun := (⇑) invFun f := mk (Function.support f).toFinite.toFinset f fun _a => Set.Finite.mem_toFinset _ - left_inv _f := ext fun _x => rfl - right_inv _f := rfl @[simp] theorem equivFunOnFinite_symm_coe {α} [Finite α] (f : α →₀ M) : equivFunOnFinite.symm f = f := diff --git a/Mathlib/Data/Fintype/BigOperators.lean b/Mathlib/Data/Fintype/BigOperators.lean index 12f6dfa7e49f44..c3ac005d61bed7 100644 --- a/Mathlib/Data/Fintype/BigOperators.lean +++ b/Mathlib/Data/Fintype/BigOperators.lean @@ -239,7 +239,7 @@ variable {α₁ : Type*} {α₂ : Type*} {M : Type*} [Fintype α₁] [Fintype α @[to_additive] theorem Fintype.prod_sumElim (f : α₁ → M) (g : α₂ → M) : ∏ x, Sum.elim f g x = (∏ a₁, f a₁) * ∏ a₂, g a₂ := - prod_disj_sum _ _ _ + prod_disjSum _ _ _ @[deprecated (since := "2025-02-20")] alias prod_sum_elim := prod_sumElim @[deprecated (since := "2025-02-20")] alias sum_sum_elim := sum_sumElim @@ -247,7 +247,7 @@ theorem Fintype.prod_sumElim (f : α₁ → M) (g : α₂ → M) : @[to_additive (attr := simp)] theorem Fintype.prod_sum_type (f : α₁ ⊕ α₂ → M) : ∏ x, f x = (∏ a₁, f (Sum.inl a₁)) * ∏ a₂, f (Sum.inr a₂) := - prod_disj_sum _ _ _ + prod_disjSum _ _ _ /-- The product over a product type equals the product of the fiberwise products. For rewriting in the reverse direction, use `Fintype.prod_prod_type'`. -/ diff --git a/Mathlib/Data/Fintype/Card.lean b/Mathlib/Data/Fintype/Card.lean index 672ca56f8eb38f..5a346cd3711ca5 100644 --- a/Mathlib/Data/Fintype/Card.lean +++ b/Mathlib/Data/Fintype/Card.lean @@ -489,9 +489,7 @@ theorem Fintype.card_fin_lt_of_le {m n : ℕ} (h : m ≤ n) : conv_rhs => rw [← Fintype.card_fin m] apply Fintype.card_congr exact { toFun := fun ⟨⟨i, _⟩, hi⟩ ↦ ⟨i, hi⟩ - invFun := fun ⟨i, hi⟩ ↦ ⟨⟨i, lt_of_lt_of_le hi h⟩, hi⟩ - left_inv := fun i ↦ rfl - right_inv := fun i ↦ rfl } + invFun := fun ⟨i, hi⟩ ↦ ⟨⟨i, lt_of_lt_of_le hi h⟩, hi⟩ } theorem Finset.card_fin (n : ℕ) : #(univ : Finset (Fin n)) = n := by simp diff --git a/Mathlib/Data/Fintype/EquivFin.lean b/Mathlib/Data/Fintype/EquivFin.lean index 1fc9363b110803..936759279af26c 100644 --- a/Mathlib/Data/Fintype/EquivFin.lean +++ b/Mathlib/Data/Fintype/EquivFin.lean @@ -358,8 +358,6 @@ alias equiv_of_fintype_self_embedding_to_embedding := toEmbedding_equivOfFiniteS (α ↪ α) ≃ (α ≃ α) where toFun e := e.equivOfFiniteSelfEmbedding invFun e := e.toEmbedding - left_inv e := rfl - right_inv e := by ext; rfl /-- A constructive embedding of a fintype `α` in another fintype `β` when `card α ≤ card β`. -/ def truncOfCardLE [Fintype α] [Fintype β] [DecidableEq α] [DecidableEq β] diff --git a/Mathlib/Data/Fintype/List.lean b/Mathlib/Data/Fintype/List.lean index 471523d8a85208..0d44cb490d5dca 100644 --- a/Mathlib/Data/Fintype/List.lean +++ b/Mathlib/Data/Fintype/List.lean @@ -100,7 +100,7 @@ instance fintypeNodupList [Fintype α] : Fintype { l : List α // l.Nodup } := b constructor · intro h rcases h with ⟨f, hf⟩ - convert Set.mem_def.mpr f.nodup + convert f.nodup rw [hf] rfl · intro h diff --git a/Mathlib/Data/Fintype/Sets.lean b/Mathlib/Data/Fintype/Sets.lean index af04803ae323d9..fc82fa40d09a6d 100644 --- a/Mathlib/Data/Fintype/Sets.lean +++ b/Mathlib/Data/Fintype/Sets.lean @@ -79,6 +79,9 @@ theorem toFinset_inj {s t : Set α} [Fintype s] [Fintype t] : s.toFinset = t.toF theorem toFinset_subset_toFinset [Fintype s] [Fintype t] : s.toFinset ⊆ t.toFinset ↔ s ⊆ t := by simp [Finset.subset_iff, Set.subset_def] +@[gcongr] +alias ⟨_, toFinset_subset_toFinset_of_subset⟩ := toFinset_subset_toFinset + @[simp] theorem toFinset_ssubset [Fintype s] {t : Finset α} : s.toFinset ⊂ t ↔ s ⊂ t := by rw [← Finset.coe_ssubset, coe_toFinset] diff --git a/Mathlib/Data/Int/CardIntervalMod.lean b/Mathlib/Data/Int/CardIntervalMod.lean index ce3c56a7bda0fd..832ff25581beb6 100644 --- a/Mathlib/Data/Int/CardIntervalMod.lean +++ b/Mathlib/Data/Int/CardIntervalMod.lean @@ -124,7 +124,7 @@ theorem count_modEq_card_eq_ceil (v : ℕ) : b.count (· ≡ v [MOD r]) = ⌈(b - (v % r : ℕ)) / (r : ℚ)⌉ := by have hr' : 0 < (r : ℚ) := by positivity rw [count_eq_card_filter_range, ← Ico_zero_eq_range, Ico_filter_modEq_card _ _ hr, - max_eq_left (sub_nonneg.mpr <| by gcongr <;> positivity)] + max_eq_left (sub_nonneg.mpr <| by gcongr; positivity)] conv_lhs => rw [← div_add_mod v r, cast_add, cast_mul, add_comm] tactic => simp_rw [← sub_sub, sub_div (_ - _), mul_div_cancel_left₀ _ hr'.ne', diff --git a/Mathlib/Data/Int/Cast/Lemmas.lean b/Mathlib/Data/Int/Cast/Lemmas.lean index 7bb426d3746c96..9e27e1dddf1991 100644 --- a/Mathlib/Data/Int/Cast/Lemmas.lean +++ b/Mathlib/Data/Int/Cast/Lemmas.lean @@ -4,7 +4,6 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Mario Carneiro -/ import Mathlib.Algebra.Group.TypeTags.Hom -import Mathlib.Algebra.Ring.Hom.Basic import Mathlib.Algebra.Ring.Int.Defs import Mathlib.Algebra.Ring.Parity @@ -119,7 +118,7 @@ lemma _root_.Odd.intCast (hn : Odd n) : Odd (n : α) := hn.map (castRingHom α) end Ring theorem cast_dvd_cast [Ring α] (m n : ℤ) (h : m ∣ n) : (m : α) ∣ (n : α) := - RingHom.map_dvd (Int.castRingHom α) h + map_dvd (Int.castRingHom α) h end cast diff --git a/Mathlib/Data/Matrix/Composition.lean b/Mathlib/Data/Matrix/Composition.lean index a2e0f9853375a2..39eb98c0bde267 100644 --- a/Mathlib/Data/Matrix/Composition.lean +++ b/Mathlib/Data/Matrix/Composition.lean @@ -33,8 +33,6 @@ variable (I J K L R R' : Type*) def comp : Matrix I J (Matrix K L R) ≃ Matrix (I × K) (J × L) R where toFun m ik jl := m ik.1 jl.1 ik.2 jl.2 invFun n i j k l := n (i, k) (j, l) - left_inv _ := rfl - right_inv _ := rfl section Basic variable {R I J K L} diff --git a/Mathlib/Data/Matrix/DualNumber.lean b/Mathlib/Data/Matrix/DualNumber.lean index 22d54e1cafe273..29b7648e91535c 100644 --- a/Mathlib/Data/Matrix/DualNumber.lean +++ b/Mathlib/Data/Matrix/DualNumber.lean @@ -23,8 +23,6 @@ open Matrix TrivSqZeroExt def Matrix.dualNumberEquiv : Matrix n n (DualNumber R) ≃ₐ[R] DualNumber (Matrix n n R) where toFun A := ⟨of fun i j => (A i j).fst, of fun i j => (A i j).snd⟩ invFun d := of fun i j => (d.fst i j, d.snd i j) - left_inv _ := Matrix.ext fun _ _ => TrivSqZeroExt.ext rfl rfl - right_inv _ := TrivSqZeroExt.ext (Matrix.ext fun _ _ => rfl) (Matrix.ext fun _ _ => rfl) map_mul' A B := by ext · dsimp [mul_apply] diff --git a/Mathlib/Data/Matrix/Kronecker.lean b/Mathlib/Data/Matrix/Kronecker.lean index 4cfbbd38bef877..c0fa0b4eaa0453 100644 --- a/Mathlib/Data/Matrix/Kronecker.lean +++ b/Mathlib/Data/Matrix/Kronecker.lean @@ -46,7 +46,7 @@ These require `open Kronecker`: namespace Matrix open scoped RightActions -variable {R α α' β β' γ γ' : Type*} +variable {R S α α' β β' γ γ' : Type*} variable {l m n p : Type*} {q r : Type*} {l' m' n' p' : Type*} section KroneckerMap @@ -182,10 +182,12 @@ theorem kroneckerMap_assoc₁ {δ ξ ω : Type*} (f : α → β → γ) (g : γ /-- When `f` is bilinear then `Matrix.kroneckerMap f` is also bilinear. -/ @[simps!] -def kroneckerMapBilinear [CommSemiring R] [AddCommMonoid α] [AddCommMonoid β] [AddCommMonoid γ] - [Module R α] [Module R β] [Module R γ] (f : α →ₗ[R] β →ₗ[R] γ) : - Matrix l m α →ₗ[R] Matrix n p β →ₗ[R] Matrix (l × n) (m × p) γ := - LinearMap.mk₂ R (kroneckerMap fun r s => f r s) (kroneckerMap_add_left _ <| f.map_add₂) +def kroneckerMapBilinear [Semiring S] [Semiring R] + [AddCommMonoid α] [AddCommMonoid β] [AddCommMonoid γ] + [Module R α] [Module R γ] [Module S β] [Module S γ] [SMulCommClass S R γ] + (f : α →ₗ[R] β →ₗ[S] γ) : + Matrix l m α →ₗ[R] Matrix n p β →ₗ[S] Matrix (l × n) (m × p) γ := + LinearMap.mk₂' R S (kroneckerMap fun r s => f r s) (kroneckerMap_add_left _ <| f.map_add₂) (fun _ => kroneckerMap_smul_left _ _ <| f.map_smul₂ _) (kroneckerMap_add_right _ fun a => (f a).map_add) fun r => kroneckerMap_smul_right _ _ fun a => (f a).map_smul r @@ -193,9 +195,10 @@ def kroneckerMapBilinear [CommSemiring R] [AddCommMonoid α] [AddCommMonoid β] /-- `Matrix.kroneckerMapBilinear` commutes with `*` if `f` does. This is primarily used with `R = ℕ` to prove `Matrix.mul_kronecker_mul`. -/ -theorem kroneckerMapBilinear_mul_mul [CommSemiring R] [Fintype m] [Fintype m'] +theorem kroneckerMapBilinear_mul_mul [Semiring S] [Semiring R] [Fintype m] [Fintype m'] [NonUnitalNonAssocSemiring α] [NonUnitalNonAssocSemiring β] [NonUnitalNonAssocSemiring γ] - [Module R α] [Module R β] [Module R γ] (f : α →ₗ[R] β →ₗ[R] γ) + [Module R α] [Module R γ] [Module S β] [Module S γ] [SMulCommClass S R γ] + (f : α →ₗ[R] β →ₗ[S] γ) (h_comm : ∀ a b a' b', f (a * b) (a' * b') = f a a' * f b b') (A : Matrix l m α) (B : Matrix m n α) (A' : Matrix l' m' β) (B' : Matrix m' n' β) : kroneckerMapBilinear f (A * B) (A' * B') = @@ -208,9 +211,11 @@ theorem kroneckerMapBilinear_mul_mul [CommSemiring R] [Fintype m] [Fintype m'] /-- `trace` distributes over `Matrix.kroneckerMapBilinear`. This is primarily used with `R = ℕ` to prove `Matrix.trace_kronecker`. -/ -theorem trace_kroneckerMapBilinear [CommSemiring R] [Fintype m] [Fintype n] [AddCommMonoid α] - [AddCommMonoid β] [AddCommMonoid γ] [Module R α] [Module R β] [Module R γ] - (f : α →ₗ[R] β →ₗ[R] γ) (A : Matrix m m α) (B : Matrix n n β) : +theorem trace_kroneckerMapBilinear [Semiring S] [Semiring R] [Fintype m] [Fintype n] + [AddCommMonoid α] [AddCommMonoid β] [AddCommMonoid γ] + [Module R α] [Module R γ] [Module S β] [Module S γ] [SMulCommClass S R γ] + (f : α →ₗ[R] β →ₗ[S] γ) + (A : Matrix m m α) (B : Matrix n n β) : trace (kroneckerMapBilinear f A B) = f (trace A) (trace B) := by simp_rw [Matrix.trace, Matrix.diag, kroneckerMapBilinear_apply_apply, LinearMap.map_sum₂, map_sum, ← Finset.univ_product_univ, Finset.sum_product, kroneckerMap_apply] @@ -218,10 +223,10 @@ theorem trace_kroneckerMapBilinear [CommSemiring R] [Fintype m] [Fintype n] [Add /-- `determinant` of `Matrix.kroneckerMapBilinear`. This is primarily used with `R = ℕ` to prove `Matrix.det_kronecker`. -/ -theorem det_kroneckerMapBilinear [CommSemiring R] [Fintype m] [Fintype n] [DecidableEq m] - [DecidableEq n] [NonAssocSemiring α] [NonAssocSemiring β] [CommRing γ] [Module R α] [Module R β] - [Module R γ] - (f : α →ₗ[R] β →ₗ[R] γ) (h_comm : ∀ a b a' b', f (a * b) (a' * b') = f a a' * f b b') +theorem det_kroneckerMapBilinear [Semiring S] [Semiring R] [Fintype m] [Fintype n] [DecidableEq m] + [DecidableEq n] [NonAssocSemiring α] [NonAssocSemiring β] [CommRing γ] [Module R α] [Module S β] + [Module R γ] [Module S γ] [SMulCommClass S R γ] + (f : α →ₗ[R] β →ₗ[S] γ) (h_comm : ∀ a b a' b', f (a * b) (a' * b') = f a a' * f b b') (A : Matrix m m α) (B : Matrix n n β) : det (kroneckerMapBilinear f A B) = det (A.map fun a => f a 1) ^ Fintype.card n * det (B.map fun b => f 1 b) ^ Fintype.card m := @@ -408,7 +413,8 @@ section Module suppress_compilation -variable [CommSemiring R] [AddCommMonoid α] [AddCommMonoid β] [AddCommMonoid γ] +variable [CommSemiring R] +variable [AddCommMonoid α] [AddCommMonoid β] [AddCommMonoid γ] variable [Module R α] [Module R β] [Module R γ] /-- The Kronecker tensor product. This is just a shorthand for `kroneckerMap (⊗ₜ)`. @@ -430,14 +436,16 @@ theorem kroneckerTMul_apply (A : Matrix l m α) (B : Matrix n p β) (i₁ i₂ j (A ⊗ₖₜ B) (i₁, i₂) (j₁, j₂) = A i₁ j₁ ⊗ₜ[R] B i₂ j₂ := rfl +variable (S) in /-- `Matrix.kronecker` as a bilinear map. -/ -def kroneckerTMulBilinear : - Matrix l m α →ₗ[R] Matrix n p β →ₗ[R] Matrix (l × n) (m × p) (α ⊗[R] β) := - kroneckerMapBilinear (TensorProduct.mk R α β) +def kroneckerTMulBilinear [Semiring S] [Module S α] [SMulCommClass R S α] : + Matrix l m α →ₗ[S] Matrix n p β →ₗ[R] Matrix (l × n) (m × p) (α ⊗[R] β) := + kroneckerMapBilinear (AlgebraTensorModule.mk _ _ α β) @[simp] -theorem kroneckerTMulBilinear_apply (A : Matrix l m α) (B : Matrix n p β) : - kroneckerTMulBilinear R A B = A ⊗ₖₜ B := rfl +theorem kroneckerTMulBilinear_apply [Semiring S] [Module S α] [SMulCommClass R S α] + (A : Matrix l m α) (B : Matrix n p β) : + kroneckerTMulBilinear R S A B = A ⊗ₖₜ[R] B := rfl /-! What follows is a copy, in order, of every `Matrix.kroneckerMap` lemma above that has hypotheses which can be filled by properties of `⊗ₜ`. -/ @@ -453,16 +461,19 @@ theorem add_kroneckerTMul (A₁ A₂ : Matrix l m α) (B : Matrix n p α) : (A₁ + A₂) ⊗ₖₜ[R] B = A₁ ⊗ₖₜ B + A₂ ⊗ₖₜ B := kroneckerMap_add_left _ add_tmul _ _ _ -theorem kroneckerTMul_add (A : Matrix l m α) (B₁ B₂ : Matrix n p α) : +theorem kroneckerTMul_add (A : Matrix l m α) (B₁ B₂ : Matrix n p β) : A ⊗ₖₜ[R] (B₁ + B₂) = A ⊗ₖₜ B₁ + A ⊗ₖₜ B₂ := kroneckerMap_add_right _ tmul_add _ _ _ -theorem smul_kroneckerTMul (r : R) (A : Matrix l m α) (B : Matrix n p α) : - (r • A) ⊗ₖₜ[R] B = r • A ⊗ₖₜ B := +theorem smul_kroneckerTMul [Monoid S] [DistribMulAction S α] [SMulCommClass R S α] + (r : S) (A : Matrix l m α) (B : Matrix n p β) : + (r • A) ⊗ₖₜ[R] B = r • A ⊗ₖₜ[R] B := kroneckerMap_smul_left _ _ (fun _ _ => smul_tmul' _ _ _) _ _ -theorem kroneckerTMul_smul (r : R) (A : Matrix l m α) (B : Matrix n p α) : - A ⊗ₖₜ[R] (r • B) = r • A ⊗ₖₜ B := +theorem kroneckerTMul_smul [Monoid S] [DistribMulAction S α] [DistribMulAction S β] + [SMul S R] [SMulCommClass R S α] [IsScalarTower S R α] [IsScalarTower S R β] + (r : S) (A : Matrix l m α) (B : Matrix n p β) : + A ⊗ₖₜ[R] (r • B) = r • A ⊗ₖₜ[R] B := kroneckerMap_smul_right _ _ (fun _ _ => tmul_smul _ _ _) _ _ theorem single_kroneckerTMul_single @@ -474,15 +485,15 @@ theorem single_kroneckerTMul_single @[deprecated (since := "2025-05-05")] alias stdBasisMatrix_kroneckerTMul_stdBasisMatrix := single_kroneckerTMul_single -theorem diagonal_kroneckerTMul_diagonal [DecidableEq m] [DecidableEq n] (a : m → α) (b : n → α) : +theorem diagonal_kroneckerTMul_diagonal [DecidableEq m] [DecidableEq n] (a : m → α) (b : n → β) : diagonal a ⊗ₖₜ[R] diagonal b = diagonal fun mn => a mn.1 ⊗ₜ b mn.2 := kroneckerMap_diagonal_diagonal _ (zero_tmul _) (tmul_zero _) _ _ -theorem kroneckerTMul_diagonal [DecidableEq n] (A : Matrix l m α) (b : n → α) : +theorem kroneckerTMul_diagonal [DecidableEq n] (A : Matrix l m α) (b : n → β) : A ⊗ₖₜ[R] diagonal b = blockDiagonal fun i => A.map fun a => a ⊗ₜ[R] b i := kroneckerMap_diagonal_right _ (tmul_zero _) _ _ -theorem diagonal_kroneckerTMul [DecidableEq l] (a : l → α) (B : Matrix m n α) : +theorem diagonal_kroneckerTMul [DecidableEq l] (a : l → α) (B : Matrix m n β) : diagonal a ⊗ₖₜ[R] B = Matrix.reindex (Equiv.prodComm _ _) (Equiv.prodComm _ _) (blockDiagonal fun i => B.map fun b => a i ⊗ₜ[R] b) := diff --git a/Mathlib/Data/Multiset/Basic.lean b/Mathlib/Data/Multiset/Basic.lean index e3dfd643410c6c..80ea037876294e 100644 --- a/Mathlib/Data/Multiset/Basic.lean +++ b/Mathlib/Data/Multiset/Basic.lean @@ -160,7 +160,6 @@ def subsingletonEquiv [Subsingleton α] : List α ≃ Multiset α where invFun := (Quot.lift id) fun (a b : List α) (h : a ~ b) => (List.ext_get h.length_eq) fun _ _ _ => Subsingleton.elim _ _ - left_inv _ := rfl right_inv m := Quot.inductionOn m fun _ => rfl @[simp] diff --git a/Mathlib/Data/Multiset/Fintype.lean b/Mathlib/Data/Multiset/Fintype.lean index 8ade6b2a926a04..d918c8e1671ac6 100644 --- a/Mathlib/Data/Multiset/Fintype.lean +++ b/Mathlib/Data/Multiset/Fintype.lean @@ -159,12 +159,6 @@ def coeEquiv (m : Multiset α) : m ≃ m.toEnumFinset where ⟨x.1.1, x.1.2, by rw [← Multiset.mem_toEnumFinset] exact x.2⟩ - left_inv := by - rintro ⟨x, i, h⟩ - rfl - right_inv := by - rintro ⟨⟨x, i⟩, h⟩ - rfl @[simp] theorem toEmbedding_coeEquiv_trans (m : Multiset α) : @@ -236,8 +230,6 @@ If `s = t` then there's an equivalence between the appropriate types. def cast {s t : Multiset α} (h : s = t) : s ≃ t where toFun x := ⟨x.1, x.2.cast (by simp [h])⟩ invFun x := ⟨x.1, x.2.cast (by simp [h])⟩ - left_inv x := rfl - right_inv x := rfl instance : IsEmpty (0 : Multiset α) := Fintype.card_eq_zero_iff.mp (by simp) diff --git a/Mathlib/Data/Nat/Basic.lean b/Mathlib/Data/Nat/Basic.lean index 29a8a3ae072b02..080fdcf57d4ab9 100644 --- a/Mathlib/Data/Nat/Basic.lean +++ b/Mathlib/Data/Nat/Basic.lean @@ -6,7 +6,7 @@ Authors: Floris van Doorn, Leonardo de Moura, Jeremy Avigad, Mario Carneiro import Mathlib.Data.Nat.Init import Mathlib.Logic.Nontrivial.Defs import Mathlib.Tactic.Contrapose -import Mathlib.Tactic.GCongr.CoreAttrs +import Mathlib.Tactic.GCongr.Core import Mathlib.Util.AssertExists /-! diff --git a/Mathlib/Data/Nat/Factorial/Basic.lean b/Mathlib/Data/Nat/Factorial/Basic.lean index 95054798b45ca2..eaa8f1c49072ae 100644 --- a/Mathlib/Data/Nat/Factorial/Basic.lean +++ b/Mathlib/Data/Nat/Factorial/Basic.lean @@ -4,7 +4,6 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Mario Carneiro, Chris Hughes, Floris van Doorn, Yaël Dillies -/ import Mathlib.Data.Nat.Basic -import Mathlib.Tactic.GCongr.CoreAttrs import Mathlib.Tactic.Common import Mathlib.Tactic.Monotonicity.Attr diff --git a/Mathlib/Data/PFunctor/Univariate/Basic.lean b/Mathlib/Data/PFunctor/Univariate/Basic.lean index a2ed00a7ebd2d5..b04cd79a044862 100644 --- a/Mathlib/Data/PFunctor/Univariate/Basic.lean +++ b/Mathlib/Data/PFunctor/Univariate/Basic.lean @@ -6,16 +6,13 @@ Authors: Jeremy Avigad import Mathlib.Data.W.Basic /-! -# Polynomial functors +# Polynomial Functors -This file defines polynomial functors and the W-type construction as a -polynomial functor. (For the M-type construction, see -pfunctor/M.lean.) +This file defines polynomial functors and the W-type construction as a polynomial functor. +(For the M-type construction, see `Mathlib/Data/PFunctor/Univariate/M.lean`.) -/ --- "W", "Idx" - -universe u v v₁ v₂ v₃ +universe u v uA uB uA₁ uB₁ uA₂ uB₂ v₁ v₂ v₃ /-- A polynomial functor `P` is given by a type `A` and a family `B` of types over `A`. `P` maps any type `α` to a new type `P α`, which is defined as the sigma type `Σ x, P.B x → α`. @@ -24,26 +21,27 @@ An element of `P α` is a pair `⟨a, f⟩`, where `a` is an element of a type ` `f : B a → α`. Think of `a` as the shape of the object and `f` as an index to the relevant elements of `α`. -/ -@[pp_with_univ] +-- Note: `nolint checkUnivs` should not apply here, we really do want two separate universe levels +@[pp_with_univ, nolint checkUnivs] structure PFunctor where /-- The head type -/ - A : Type u + A : Type uA /-- The child family of types -/ - B : A → Type u + B : A → Type uB namespace PFunctor instance : Inhabited PFunctor := ⟨⟨default, default⟩⟩ -variable (P : PFunctor.{u}) {α : Type v₁} {β : Type v₂} {γ : Type v₃} +variable (P : PFunctor.{uA, uB}) {α : Type v₁} {β : Type v₂} {γ : Type v₃} /-- Applying `P` to an object of `Type` -/ @[coe] -def Obj (α : Type v) := +def Obj (α : Type v) : Type (max v uA uB) := Σ x : P.A, P.B x → α -instance : CoeFun PFunctor.{u} (fun _ => Type v → Type (max u v)) where +instance : CoeFun PFunctor.{uA, uB} (fun _ => Type v → Type (max v uA uB)) where coe := Obj /-- Applying `P` to a morphism of `Type` -/ @@ -53,7 +51,7 @@ def map (f : α → β) : P α → P β := instance Obj.inhabited [Inhabited P.A] [Inhabited α] : Inhabited (P α) := ⟨⟨default, default⟩⟩ -instance : Functor.{v, max u v} P.Obj where map := @map P +instance : Functor P.Obj where map := @map P /-- We prefer `PFunctor.map` to `Functor.map` because it is universe-polymorphic. -/ @[simp] @@ -72,35 +70,34 @@ protected theorem id_map : ∀ x : P α, P.map id x = x := fun ⟨_, _⟩ => rfl protected theorem map_map (f : α → β) (g : β → γ) : ∀ x : P α, P.map g (P.map f x) = P.map (g ∘ f) x := fun ⟨_, _⟩ => rfl -instance : LawfulFunctor.{v, max u v} P.Obj where +instance : LawfulFunctor (Obj.{v} P) where map_const := rfl id_map x := P.id_map x comp_map f g x := P.map_map f g x |>.symm -/-- re-export existing definition of W-types and -adapt it to a packaged definition of polynomial functor -/ -def W := +/-- Re-export existing definition of W-types and adapt it to a packaged definition of polynomial +functor. -/ +def W : Type (max uA uB) := WType P.B -/- inhabitants of W types is awkward to encode as an instance -assumption because there needs to be a value `a : P.A` -such that `P.B a` is empty to yield a finite tree -/ +/- Inhabitants of W types is awkward to encode as an instance assumption because there needs to be a +value `a : P.A` such that `P.B a` is empty to yield a finite tree. -/ variable {P} -/-- root element of a W tree -/ +/-- The root element of a W tree -/ def W.head : W P → P.A | ⟨a, _f⟩ => a -/-- children of the root of a W tree -/ +/-- The children of the root of a W tree -/ def W.children : ∀ x : W P, P.B (W.head x) → W P | ⟨_a, f⟩ => f -/-- destructor for W-types -/ +/-- The destructor for W-types -/ def W.dest : W P → P (W P) | ⟨a, f⟩ => ⟨a, f⟩ -/-- constructor for W-types -/ +/-- The constructor for W-types -/ def W.mk : P (W P) → W P | ⟨a, f⟩ => ⟨a, f⟩ @@ -112,10 +109,9 @@ theorem W.mk_dest (p : W P) : W.mk (W.dest p) = p := by cases p; rfl variable (P) -/-- `Idx` identifies a location inside the application of a pfunctor. -For `F : PFunctor`, `x : F α` and `i : F.Idx`, `i` can designate -one part of `x` or is invalid, if `i.1 ≠ x.1` -/ -def Idx := +/-- `Idx` identifies a location inside the application of a polynomial functor. For `F : PFunctor`, +`x : F α` and `i : F.Idx`, `i` can designate one part of `x` or is invalid, if `i.1 ≠ x.1`. -/ +def Idx : Type (max uA uB) := Σ x : P.A, P.B x instance Idx.inhabited [Inhabited P.A] [Inhabited (P.B default)] : Inhabited P.Idx := @@ -123,8 +119,7 @@ instance Idx.inhabited [Inhabited P.A] [Inhabited (P.B default)] : Inhabited P.I variable {P} -/-- `x.iget i` takes the component of `x` designated by `i` if any is or returns -a default value -/ +/-- `x.iget i` takes the component of `x` designated by `i` if any is or returns a default value -/ def Obj.iget [DecidableEq P.A] {α} [Inhabited α] (x : P α) (i : P.Idx) : α := if h : i.1 = x.1 then x.2 (cast (congr_arg _ h) i.2) else default @@ -145,16 +140,19 @@ Composition of polynomial functors. -/ namespace PFunctor -/-- functor composition for polynomial functors -/ -def comp (P₂ P₁ : PFunctor.{u}) : PFunctor.{u} := +/-- Composition for polynomial functors -/ +def comp (P₂ : PFunctor.{uA₂, uB₂}) (P₁ : PFunctor.{uA₁, uB₁}) : + PFunctor.{max uA₁ uA₂ uB₂, max uB₁ uB₂} := ⟨Σ a₂ : P₂.1, P₂.2 a₂ → P₁.1, fun a₂a₁ => Σ u : P₂.2 a₂a₁.1, P₁.2 (a₂a₁.2 u)⟩ -/-- constructor for composition -/ -def comp.mk (P₂ P₁ : PFunctor.{u}) {α : Type} (x : P₂ (P₁ α)) : comp P₂ P₁ α := +/-- Constructor for composition -/ +def comp.mk (P₂ : PFunctor.{uA₂, uB₂}) (P₁ : PFunctor.{uA₁, uB₁}) {α : Type v} (x : P₂ (P₁ α)) : + comp P₂ P₁ α := ⟨⟨x.1, Sigma.fst ∘ x.2⟩, fun a₂a₁ => (x.2 a₂a₁.1).2 a₂a₁.2⟩ -/-- destructor for composition -/ -def comp.get (P₂ P₁ : PFunctor.{u}) {α : Type} (x : comp P₂ P₁ α) : P₂ (P₁ α) := +/-- Destructor for composition -/ +def comp.get (P₂ : PFunctor.{uA₂, uB₂}) (P₁ : PFunctor.{uA₁, uB₁}) {α : Type v} (x : comp P₂ P₁ α) : + P₂ (P₁ α) := ⟨x.1.1, fun a₂ => ⟨x.1.2 a₂, fun a₁ => x.2 ⟨a₂, a₁⟩⟩⟩ end PFunctor @@ -164,7 +162,7 @@ Lifting predicates and relations. -/ namespace PFunctor -variable {P : PFunctor.{u}} +variable {P : PFunctor.{uA, uB}} open Functor diff --git a/Mathlib/Data/PFunctor/Univariate/M.lean b/Mathlib/Data/PFunctor/Univariate/M.lean index f0bcf4675d89ff..93334bcdd6deb1 100644 --- a/Mathlib/Data/PFunctor/Univariate/M.lean +++ b/Mathlib/Data/PFunctor/Univariate/M.lean @@ -13,20 +13,20 @@ as the greatest fixpoint of a polynomial functor. -/ -universe u v w +universe u uA uB v w open Nat Function open List -variable (F : PFunctor.{u}) +variable (F : PFunctor.{uA, uB}) namespace PFunctor namespace Approx /-- `CofixA F n` is an `n` level approximation of an M-type -/ -inductive CofixA : ℕ → Type u +inductive CofixA : ℕ → Type (max uA uB) | continue : CofixA 0 | intro {n} : ∀ a, (F.B a → CofixA n) → CofixA (succ n) @@ -117,7 +117,7 @@ theorem P_corec (i : X) (n : ℕ) : Agree (sCorec f i n) (sCorec f i (succ n)) : apply n_ih /-- `Path F` provides indices to access internal nodes in `Corec F` -/ -def Path (F : PFunctor.{u}) := +def Path (F : PFunctor.{uA, uB}) := List F.Idx instance Path.inhabited : Inhabited (Path F) := @@ -578,7 +578,7 @@ universe u' v' def corecOn {X : Type*} (x₀ : X) (f : X → F X) : M F := M.corec f x₀ -variable {P : PFunctor.{u}} {α : Type*} +variable {P : PFunctor.{uA, uB}} {α : Type*} theorem dest_corec (g : α → P α) (x : α) : M.dest (M.corec g x) = P.map (M.corec g) (g x) := by rw [corec_def, dest_mk] @@ -643,10 +643,10 @@ def corec₁ {α : Type u} (F : ∀ X, (α → X) → α → P X) : α → M P : /-- corecursor where it is possible to return a fully formed value at any point of the computation -/ -def corec' {α : Type u} (F : ∀ {X : Type u}, (α → X) → α → M P ⊕ P X) (x : α) : M P := +def corec' {α : Type u} (F : ∀ {X : Type (max u uA uB)}, (α → X) → α → M P ⊕ P X) (x : α) : M P := corec₁ (fun _ rec (a : M P ⊕ α) => - let y := a >>= F (rec ∘ Sum.inr) + let y := Sum.bind a (F (rec ∘ Sum.inr)) match y with | Sum.inr y => y | Sum.inl y => P.map (rec ∘ Sum.inl) (M.dest y)) diff --git a/Mathlib/Data/QPF/Univariate/Basic.lean b/Mathlib/Data/QPF/Univariate/Basic.lean index cb7bf23aa1f65d..ca91481189b915 100644 --- a/Mathlib/Data/QPF/Univariate/Basic.lean +++ b/Mathlib/Data/QPF/Univariate/Basic.lean @@ -38,7 +38,7 @@ The present theory focuses on the univariate case for qpfs -/ -universe u +universe u u' v /-- Quotients of polynomial functors. @@ -46,8 +46,8 @@ Roughly speaking, saying that `F` is a quotient of a polynomial functor means th elements of `F α` are represented by pairs `⟨a, f⟩`, where `a` is the shape of the object and `f` indexes the relevant elements of `α`, in a suitably natural manner. -/ -class QPF (F : Type u → Type u) extends Functor F where - P : PFunctor.{u} +class QPF (F : Type u → Type v) extends Functor F where + P : PFunctor.{u, u'} abs : ∀ {α}, P α → F α repr : ∀ {α}, F α → P α abs_repr : ∀ {α} (x : F α), abs (repr x) = x @@ -55,7 +55,7 @@ class QPF (F : Type u → Type u) extends Functor F where namespace QPF -variable {F : Type u → Type u} [q : QPF F] +variable {F : Type u → Type v} [q : QPF F] open Functor (Liftp Liftr) @@ -221,6 +221,8 @@ attribute [local instance] Wsetoid def Fix (F : Type u → Type u) [q : QPF F] := Quotient (Wsetoid : Setoid q.P.W) +variable {F : Type u → Type u} [q : QPF F] + /-- recursor of a type defined by a qpf -/ def Fix.rec {α : Type _} (g : F α → α) : Fix F → α := Quot.lift (recF g) (recF_eq_of_Wequiv g) diff --git a/Mathlib/Data/Rat/Defs.lean b/Mathlib/Data/Rat/Defs.lean index 297e8f1c6587bc..1ddb179abdb8f9 100644 --- a/Mathlib/Data/Rat/Defs.lean +++ b/Mathlib/Data/Rat/Defs.lean @@ -16,8 +16,8 @@ import Mathlib.Data.Nat.Basic ## Summary We define the integral domain structure on `ℚ` and prove basic lemmas about it. -The definition of the field structure on `ℚ` will be done in `Mathlib/Data/Rat/Basic.lean` once the -`Field` class has been defined. +The definition of the field structure on `ℚ` will be done in `Mathlib/Algebra/Field/Rat.lean` +once the `Field` class has been defined. ## Main Definitions diff --git a/Mathlib/Data/Set/Accumulate.lean b/Mathlib/Data/Set/Accumulate.lean index 64d874f0e99e64..62c13904f1e9e1 100644 --- a/Mathlib/Data/Set/Accumulate.lean +++ b/Mathlib/Data/Set/Accumulate.lean @@ -8,7 +8,11 @@ import Mathlib.Data.Set.Lattice /-! # Accumulate -The function `Accumulate` takes a set `s` and returns `⋃ y ≤ x, s y`. +The function `Accumulate` takes `s : α → Set β` with `LE α` and returns `⋃ y ≤ x, s y`. + +In large parts, this file is parallel to `Mathlib.Data.Set.Dissipate`, where +`Dissipate s := ⋂ y ≤ x, s y` is defined. + -/ @@ -16,7 +20,7 @@ variable {α β : Type*} {s : α → Set β} namespace Set -/-- `Accumulate s` is the union of `s y` for `y ≤ x`. -/ +/-- `Accumulate s x` is the union of `s y` for `y ≤ x`. -/ def Accumulate [LE α] (s : α → Set β) (x : α) : Set β := ⋃ y ≤ x, s y diff --git a/Mathlib/Data/Set/Basic.lean b/Mathlib/Data/Set/Basic.lean index bc4092476c09c5..370692228f402e 100644 --- a/Mathlib/Data/Set/Basic.lean +++ b/Mathlib/Data/Set/Basic.lean @@ -192,6 +192,7 @@ instance : Inhabited (Set α) := theorem mem_of_mem_of_subset {x : α} {s t : Set α} (hx : x ∈ s) (h : s ⊆ t) : x ∈ t := h hx +@[deprecated forall_swap (since := "2025-06-10")] theorem forall_in_swap {p : α → β → Prop} : (∀ a ∈ s, ∀ (b), p a b) ↔ ∀ (b), ∀ a ∈ s, p a b := by tauto @@ -201,35 +202,18 @@ theorem setOf_inj {p q : α → Prop} : { x | p x } = { x | q x } ↔ p = q := I /-! ### Lemmas about `mem` and `setOf` -/ -theorem mem_setOf {a : α} {p : α → Prop} : a ∈ { x | p x } ↔ p a := - Iff.rfl - -/-- This lemma is intended for use with `rw` where a membership predicate is needed, -hence the explicit argument and the equality in the reverse direction from normal. -See also `Set.mem_setOf_eq` for the reverse direction applied to an argument. -/ -theorem eq_mem_setOf (p : α → Prop) : p = (· ∈ {a | p a}) := rfl - -/-- If `h : a ∈ {x | p x}` then `h.out : p x`. These are definitionally equal, but this can -nevertheless be useful for various reasons, e.g. to apply further projection notation or in an -argument to `simp`. -/ -theorem _root_.Membership.mem.out {p : α → Prop} {a : α} (h : a ∈ { x | p x }) : p a := - h - -theorem notMem_setOf_iff {a : α} {p : α → Prop} : a ∉ { x | p x } ↔ ¬p a := - Iff.rfl - -@[deprecated (since := "2025-05-24")] alias nmem_setOf_iff := notMem_setOf_iff - -@[simp] -theorem setOf_mem_eq {s : Set α} : { x | x ∈ s } = s := - rfl - +@[deprecated "This lemma abuses the `Set α := α → Prop` defeq. +If you think you need it you have already taken a wrong turn." (since := "2025-06-10")] theorem setOf_set {s : Set α} : setOf s = s := rfl +@[deprecated "This lemma abuses the `Set α := α → Prop` defeq. +If you think you need it you have already taken a wrong turn." (since := "2025-06-10")] theorem setOf_app_iff {p : α → Prop} {x : α} : { x | p x } x ↔ p x := Iff.rfl +@[deprecated "This lemma abuses the `Set α := α → Prop` defeq. +If you think you need it you have already taken a wrong turn." (since := "2025-06-10")] theorem mem_def {a : α} {s : Set α} : a ∈ s ↔ s a := Iff.rfl @@ -246,6 +230,9 @@ theorem setOf_subset {p : α → Prop} {s : Set α} : setOf p ⊆ s ↔ ∀ x, p theorem setOf_subset_setOf {p q : α → Prop} : { a | p a } ⊆ { a | q a } ↔ ∀ a, p a → q a := Iff.rfl +@[gcongr] +alias ⟨_, setOf_subset_setOf_of_imp⟩ := setOf_subset_setOf + theorem setOf_and {p q : α → Prop} : { a | p a ∧ q a } = { a | p a } ∩ { a | q a } := rfl @@ -318,7 +305,7 @@ theorem Subset.antisymm_iff {a b : Set α} : a = b ↔ a ⊆ b ∧ b ⊆ a := theorem eq_of_subset_of_subset {a b : Set α} : a ⊆ b → b ⊆ a → a = b := Subset.antisymm -theorem mem_of_subset_of_mem {s₁ s₂ : Set α} {a : α} (h : s₁ ⊆ s₂) : a ∈ s₁ → a ∈ s₂ := +@[gcongr] theorem mem_of_subset_of_mem {s₁ s₂ : Set α} {a : α} (h : s₁ ⊆ s₂) : a ∈ s₁ → a ∈ s₂ := @h _ theorem notMem_subset (h : s ⊆ t) : a ∉ t → a ∉ s := @@ -393,7 +380,7 @@ protected noncomputable def Nonempty.some (h : s.Nonempty) : α := protected theorem Nonempty.some_mem (h : s.Nonempty) : h.some ∈ s := Classical.choose_spec h -theorem Nonempty.mono (ht : s ⊆ t) (hs : s.Nonempty) : t.Nonempty := +@[gcongr] theorem Nonempty.mono (ht : s ⊆ t) (hs : s.Nonempty) : t.Nonempty := hs.imp ht theorem nonempty_of_not_subset (h : ¬s ⊆ t) : (s \ t).Nonempty := @@ -1509,5 +1496,3 @@ protected lemma setSubtypeComm_symm_apply (p : α → Prop) (s : {s // ∀ a ∈ rfl end Equiv - -set_option linter.style.longFile 1700 diff --git a/Mathlib/Data/Set/Dissipate.lean b/Mathlib/Data/Set/Dissipate.lean new file mode 100644 index 00000000000000..43c4ecb7848536 --- /dev/null +++ b/Mathlib/Data/Set/Dissipate.lean @@ -0,0 +1,157 @@ +/- +Copyright (c) 2025 Peter Pfaffelhuber. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Peter Pfaffelhuber +-/ +import Mathlib.Data.Set.Lattice +import Mathlib.Order.Directed +import Mathlib.MeasureTheory.PiSystem + +/-! +# Dissipate + +The function `Dissipate` takes `s : α → Set β` with `LE α` and returns `⋂ y ≤ x, s y`. + +In large parts, this file is parallel to `Mathlib.Data.Set.Accumulate`, where +`Accumulate s := ⋃ y ≤ x, s y` is defined. + +-/ + +open Nat + +variable {α β : Type*} {s : α → Set β} + +namespace Set + +/-- `Dissipate s x` is the intersection of `s y` for `y ≤ x`. -/ +def Dissipate [LE α] (s : α → Set β) (x : α) : Set β := + ⋂ y ≤ x, s y + +theorem dissipate_def [LE α] {x : α} : Dissipate s x = ⋂ y ≤ x, s y := rfl + +theorem dissipate_eq {s : ℕ → Set β} {n : ℕ} : Dissipate s n = ⋂ k < n + 1, s k := by + simp_rw [Nat.lt_add_one_iff] + rfl + +theorem mem_dissipate [LE α] {x : α} {z : β} : z ∈ Dissipate s x ↔ ∀ y ≤ x, z ∈ s y := by + simp only [dissipate_def, mem_iInter] + +theorem dissipate_subset [Preorder α] {x y : α} (hy : y ≤ x) : Dissipate s x ⊆ s y := + biInter_subset_of_mem hy + +theorem dissipate_subset_iInter [Preorder α] (x : α) : ⋂ i, s i ⊆ Dissipate s x := by + simp only [Dissipate, subset_iInter_iff] + exact fun x h ↦ iInter_subset_of_subset x fun ⦃a⦄ a ↦ a + +theorem dissipate_antitone [Preorder α] : Antitone (Dissipate s) := + fun _ _ hab ↦ biInter_subset_biInter_left fun _ hz => le_trans hz hab + +@[gcongr] +theorem dissipate_subset_dissipate [Preorder α] {x y} (h : y ≤ x) : + Dissipate s x ⊆ Dissipate s y := + dissipate_antitone h + +@[simp] +theorem biInter_dissipate [Preorder α] {s : α → Set β} {x : α} : + Dissipate (Dissipate s) x = Dissipate s x := by + apply Subset.antisymm + · apply iInter_mono fun z y hy ↦ ?_ + simp only [dissipate_def, mem_iInter] at * + exact fun h ↦ hy h z <| le_refl z + · simp only [subset_iInter_iff, Dissipate] + exact fun i hi z hz ↦ biInter_subset_of_mem <| le_trans hz hi + +@[simp] +theorem iInter_dissipate [Preorder α] : ⋂ x, Dissipate s x = ⋂ x, s x := by + simp_rw [dissipate_def] + apply Subset.antisymm <;> simp_rw [subset_def, mem_iInter] + · exact fun z h x' ↦ h x' x' (le_refl x') + · exact fun z h x' y hy ↦ h y + +@[simp] +lemma dissipate_bot [PartialOrder α] [OrderBot α] (s : α → Set β) : Dissipate s ⊥ = s ⊥ := by + simp only [dissipate_def, le_bot_iff, iInter_iInter_eq_left] + +open Nat + +@[simp] +theorem dissipate_succ (s : ℕ → Set α) (n : ℕ) : + ⋂ y, ⋂ (_ : y ≤ n + 1), s y = (⋂ y, ⋂ (_ : y ≤ n), s y) ∩ s (n + 1) + := by + ext x + refine ⟨fun hx ↦ ?_, fun hx ↦ ?_⟩ + · simp only [mem_inter_iff, mem_iInter, Dissipate] at * + exact ⟨fun i hi ↦ hx i <| le_trans hi <| + le_add_right n 1, hx (n + 1) <| le_refl (n + 1)⟩ + · simp only [Dissipate, mem_inter_iff, mem_iInter] at * + intro i hi + by_cases h : i ≤ n + · exact hx.1 i h + · simp only [not_le] at h + exact le_antisymm hi h ▸ hx.2 + +lemma dissipate_zero (s : ℕ → Set β) : Dissipate s 0 = s 0 := by + simp [dissipate_def] + +lemma exists_subset_dissipate_of_directed {s : ℕ → Set α} + (hd : Directed (fun (x1 x2 : Set α) => x2 ⊆ x1) s) (n : ℕ) : ∃ m, s m ⊆ Dissipate s n := by + induction n with + | zero => use 0; simp [dissipate_def] + | succ n hn => + obtain ⟨m, hm⟩ := hn + simp_rw [dissipate_def, dissipate_succ] + obtain ⟨k, hk⟩ := hd m (n+1) + simp at hk + use k + simp only [subset_inter_iff] + exact ⟨le_trans hk.1 hm, hk.2⟩ + +lemma exists_dissipate_eq_empty_iff {s : ℕ → Set α} + (hd : Directed (fun (x1 x2 : Set α) => x2 ⊆ x1) s) : + (∃ n, Dissipate s n = ∅) ↔ (∃ n, s n = ∅) := by + refine ⟨?_, fun ⟨n, hn⟩ ↦ ⟨n, ?_⟩⟩ + · rw [← not_imp_not] + push_neg + intro h n + obtain ⟨m, hm⟩ := exists_subset_dissipate_of_directed hd n + exact Set.Nonempty.mono hm (h m) + · by_cases hn' : n = 0 + · rw [hn'] + exact Eq.trans (dissipate_zero s) (hn' ▸ hn) + · obtain ⟨k, hk⟩ := exists_eq_add_one_of_ne_zero hn' + rw [hk, dissipate_def, dissipate_succ, ← hk, hn, Set.inter_empty] + +lemma directed_dissipate {s : ℕ → Set α} : + Directed (fun (x1 x2 : Set α) => x2 ⊆ x1) (Dissipate s) := + dissipate_antitone.directed_ge + +lemma exists_dissipate_eq_empty_iff_of_directed (C : ℕ → Set α) + (hd : Directed (fun (x1 x2 : Set α) => x2 ⊆ x1) C) : + (∃ n, C n = ∅) ↔ (∃ n, Dissipate C n = ∅) := by + refine ⟨fun ⟨n, hn⟩ ↦ ⟨n, ?_⟩ , ?_⟩ + · by_cases hn' : n = 0 + · rw [hn', dissipate_zero] + exact hn' ▸ hn + · obtain ⟨k, hk⟩ := exists_eq_succ_of_ne_zero hn' + simp_rw [hk, succ_eq_add_one, dissipate_def, dissipate_succ, + ← succ_eq_add_one, ← hk, hn, Set.inter_empty] + · rw [← not_imp_not] + push_neg + intro h n + obtain ⟨m, hm⟩ := exists_subset_dissipate_of_directed hd n + exact Set.Nonempty.mono hm (h m) + +/-- For a ∩-stable attribute `p` on `Set α` and a sequence of sets `s` with this attribute, +`p (Dissipate s n)` holds. -/ +lemma dissipate_of_piSystem {s : ℕ → Set α} {p : Set α → Prop} + (hp : IsPiSystem p) (h : ∀ n, p (s n)) (n : ℕ) (h' : (Dissipate s n).Nonempty) : + p (Dissipate s n) := by + induction n with + | zero => + simp only [dissipate_def, le_zero_eq, iInter_iInter_eq_left] + exact h 0 + | succ n hn => + rw [dissipate_def, dissipate_succ] at * + apply hp (Dissipate s n) (hn (Nonempty.left h')) (s (n+1)) (h (n+1)) h' + +end Set diff --git a/Mathlib/Data/Set/Image.lean b/Mathlib/Data/Set/Image.lean index ec020fd4b74d3c..37cc031a46de8a 100644 --- a/Mathlib/Data/Set/Image.lean +++ b/Mathlib/Data/Set/Image.lean @@ -679,6 +679,16 @@ theorem range_eq_empty_iff {f : ι → α} : range f = ∅ ↔ IsEmpty ι := by theorem range_eq_empty [IsEmpty ι] (f : ι → α) : range f = ∅ := range_eq_empty_iff.2 ‹_› +@[simp] +theorem range_eq_singleton_iff [Nonempty ι] {y} : + Set.range f = {y} ↔ ∀ (x : ι), f x = y := by + simp_rw [Set.ext_iff, Set.mem_range, Set.mem_singleton_iff] + exact ⟨fun h _ => by simp_rw [← h, exists_apply_eq_apply], + fun h _ => by simp_rw [h, exists_const, eq_comm]⟩ + +theorem range_eq_singleton [Nonempty ι] {y} (hy : ∀ (x : ι), f x = y) : + Set.range f = {y} := range_eq_singleton_iff.mpr hy + instance instNonemptyRange [Nonempty ι] (f : ι → α) : Nonempty (range f) := (range_nonempty f).to_subtype @@ -876,10 +886,8 @@ theorem range_const_subset {c : α} : (range fun _ : ι => c) ⊆ {c} := range_subset_iff.2 fun _ => rfl @[simp] -theorem range_const : ∀ [Nonempty ι] {c : α}, (range fun _ : ι => c) = {c} - | ⟨x⟩, _ => - (Subset.antisymm range_const_subset) fun _ hy => - (mem_singleton_iff.1 hy).symm ▸ mem_range_self x +theorem range_const : ∀ [Nonempty ι] {c : α}, (range fun _ : ι => c) = {c} := + range_eq_singleton (fun _ => rfl) theorem range_subtype_map {p : α → Prop} {q : β → Prop} (f : α → β) (h : ∀ x, p x → q (f x)) : range (Subtype.map f h) = (↑) ⁻¹' (f '' { x | p x }) := by diff --git a/Mathlib/Data/Set/Lattice.lean b/Mathlib/Data/Set/Lattice.lean index 066ec064507559..66f32f91335f10 100644 --- a/Mathlib/Data/Set/Lattice.lean +++ b/Mathlib/Data/Set/Lattice.lean @@ -1323,7 +1323,6 @@ noncomputable def sigmaEquiv (s : α → Set β) (hs : ∀ b, ∃! i, b ∈ s i) toFun | ⟨_, b⟩ => b invFun b := ⟨(hs b).choose, b, (hs b).choose_spec.1⟩ left_inv | ⟨i, b, hb⟩ => Sigma.subtype_ext ((hs b).choose_spec.2 i hb).symm rfl - right_inv _ := rfl /-- Equivalence between a disjoint union and a dependent sum. -/ noncomputable def unionEqSigmaOfDisjoint {t : α → Set β} diff --git a/Mathlib/Data/Set/Monotone.lean b/Mathlib/Data/Set/Monotone.lean index 861ff25e124c01..e5c4ec629ed0d7 100644 --- a/Mathlib/Data/Set/Monotone.lean +++ b/Mathlib/Data/Set/Monotone.lean @@ -85,15 +85,19 @@ protected theorem _root_.StrictAntiOn.strictAnti (h : StrictAntiOn f s) : StrictAnti (f ∘ Subtype.val : s → β) := fun x y hlt => h x.coe_prop y.coe_prop hlt -lemma MonotoneOn_insert_iff {a : α} : +lemma monotoneOn_insert_iff {a : α} : MonotoneOn f (insert a s) ↔ (∀ b ∈ s, b ≤ a → f b ≤ f a) ∧ (∀ b ∈ s, a ≤ b → f a ≤ f b) ∧ MonotoneOn f s := by simp [MonotoneOn, forall_and] -lemma AntitoneOn_insert_iff {a : α} : +@[deprecated (since := "2025-06-14")] alias MonotoneOn_insert_iff := monotoneOn_insert_iff + +lemma antitoneOn_insert_iff {a : α} : AntitoneOn f (insert a s) ↔ (∀ b ∈ s, b ≤ a → f a ≤ f b) ∧ (∀ b ∈ s, a ≤ b → f b ≤ f a) ∧ AntitoneOn f s := - @MonotoneOn_insert_iff α βᵒᵈ _ _ _ _ _ + @monotoneOn_insert_iff α βᵒᵈ _ _ _ _ _ + +@[deprecated (since := "2025-06-14")] alias AntitoneOn_insert_iff := antitoneOn_insert_iff end Mono diff --git a/Mathlib/Data/Set/Operations.lean b/Mathlib/Data/Set/Operations.lean index 1e147f909fedbf..6abeef3bfbb0c4 100644 --- a/Mathlib/Data/Set/Operations.lean +++ b/Mathlib/Data/Set/Operations.lean @@ -75,10 +75,32 @@ namespace Set variable {α : Type u} {β : Type v} {γ : Type w} +/-! ### Lemmas about `mem` and `setOf` -/ + @[simp, mfld_simps] theorem mem_setOf_eq {x : α} {p : α → Prop} : (x ∈ {y | p y}) = p x := rfl +/-- This lemma is intended for use with `rw` where a membership predicate is needed, +hence the explicit argument and the equality in the reverse direction from normal. +See also `Set.mem_setOf_eq` for the reverse direction applied to an argument. -/ +theorem eq_mem_setOf (p : α → Prop) : p = (· ∈ {a | p a}) := rfl + +theorem mem_setOf {a : α} {p : α → Prop} : a ∈ { x | p x } ↔ p a := Iff.rfl + +/-- If `h : a ∈ {x | p x}` then `h.out : p x`. These are definitionally equal, but this can +nevertheless be useful for various reasons, e.g. to apply further projection notation or in an +argument to `simp`. -/ +alias ⟨_root_.Membership.mem.out, _⟩ := mem_setOf + +theorem notMem_setOf_iff {a : α} {p : α → Prop} : a ∉ { x | p x } ↔ ¬p a := Iff.rfl + +@[deprecated (since := "2025-05-24")] alias nmem_setOf_iff := notMem_setOf_iff + +@[simp] theorem setOf_mem_eq {s : Set α} : { x | x ∈ s } = s := rfl + @[simp, mfld_simps] theorem mem_univ (x : α) : x ∈ @univ α := trivial +/-! ### Operations -/ + instance : HasCompl (Set α) := ⟨fun s ↦ {x | x ∉ s}⟩ @[simp] theorem mem_compl_iff (s : Set α) (x : α) : x ∈ sᶜ ↔ x ∉ s := Iff.rfl diff --git a/Mathlib/Data/Set/Prod.lean b/Mathlib/Data/Set/Prod.lean index ded70ca0d94e00..4d082328e57de3 100644 --- a/Mathlib/Data/Set/Prod.lean +++ b/Mathlib/Data/Set/Prod.lean @@ -698,6 +698,21 @@ theorem pi_eq_empty_iff' : s.pi t = ∅ ↔ ∃ i ∈ s, t i = ∅ := by simp [p theorem disjoint_pi : Disjoint (s.pi t₁) (s.pi t₂) ↔ ∃ i ∈ s, Disjoint (t₁ i) (t₂ i) := by simp only [disjoint_iff_inter_eq_empty, ← pi_inter_distrib, pi_eq_empty_iff'] +theorem pi_nonempty_iff' [∀ i, Decidable (i ∈ s)] : + (s.pi t).Nonempty ↔ ∀ i ∈ s, (t i).Nonempty := by + classical + rw [pi_nonempty_iff] + have h := fun i ↦ exists_mem_of_nonempty (α i) + choose y hy using h + refine ⟨fun h i hi ↦ ?_, fun h i ↦ ?_⟩ + · obtain ⟨x, hx⟩ := h i + exact ⟨x, hx hi⟩ + · choose x hx using h + use (if g : i ∈ s then x i g else y i) + intro hi + simp only [hi, ↓reduceDIte] + exact hx i hi + end Nonempty @[simp] @@ -735,6 +750,10 @@ theorem pi_if {p : ι → Prop} [h : DecidablePred p] (s : Set ι) (t₁ t₂ : theorem union_pi : (s₁ ∪ s₂).pi t = s₁.pi t ∩ s₂.pi t := by simp [pi, or_imp, forall_and, setOf_and] +theorem pi_antitone (h : s₁ ⊆ s₂) : s₂.pi t ⊆ s₁.pi t := by + rw [← union_diff_cancel h, union_pi] + exact Set.inter_subset_left + theorem union_pi_inter (ht₁ : ∀ i ∉ s₁, t₁ i = univ) (ht₂ : ∀ i ∉ s₂, t₂ i = univ) : (s₁ ∪ s₂).pi (fun i ↦ t₁ i ∩ t₂ i) = s₁.pi t₁ ∩ s₂.pi t₂ := by @@ -900,6 +919,25 @@ theorem univ_pi_ite (s : Set ι) [DecidablePred (· ∈ s)] (t : ∀ i, Set (α refine forall_congr' fun i => ?_ split_ifs with h <;> simp [h] +lemma pi_image_eq_of_subset {C : (i : ι) → Set (Set (α i))} + (hC : ∀ i, Nonempty (C i)) + {s₁ s₂ : Set ι} [∀ i : ι, Decidable (i ∈ s₁)] + (hst : s₁ ⊆ s₂) : s₁.pi '' s₁.pi C = s₁.pi '' s₂.pi C := by + let C_mem (i : ι) : Set (α i) := ((Set.exists_mem_of_nonempty (C i)).choose : Set (α i)) + have h_mem (i : ι) : C_mem i ∈ C i := by + simp [C_mem] + ext f + refine ⟨fun ⟨x, ⟨hx1, hx2⟩⟩ ↦ ?_, fun ⟨w, ⟨hw1, hw2⟩⟩ ↦ ?_⟩ + · use fun i ↦ if i ∈ s₁ then x i else C_mem i + refine ⟨fun i hi ↦ ?_, ?_⟩ + · by_cases h1 : i ∈ s₁ <;> simp only [h1, ↓reduceIte] + · exact hx1 i h1 + · exact h_mem i + · rw [← hx2] + exact pi_congr rfl (fun i hi ↦ by simp only [hi, ↓reduceIte]) + · have hw3 := pi_antitone hst hw1 + use w + end Pi end Set diff --git a/Mathlib/Data/Set/Sigma.lean b/Mathlib/Data/Set/Sigma.lean index 02ec5df0e66411..c70d7d92a7e320 100644 --- a/Mathlib/Data/Set/Sigma.lean +++ b/Mathlib/Data/Set/Sigma.lean @@ -226,6 +226,10 @@ theorem sigma_subset_preimage_fst (s : Set ι) (t : ∀ i, Set (α i)) : s.sigma theorem fst_image_sigma_subset (s : Set ι) (t : ∀ i, Set (α i)) : Sigma.fst '' s.sigma t ⊆ s := image_subset_iff.2 fun _ ↦ And.left +lemma image_sigma_eq_iUnion {γ : Type*} (f : (Σ i, α i) → γ) : + f '' (s.sigma t) = ⋃ i ∈ s, (f ∘ Sigma.mk i) '' t i := by + aesop + theorem fst_image_sigma (s : Set ι) (ht : ∀ i, (t i).Nonempty) : Sigma.fst '' s.sigma t = s := (fst_image_sigma_subset _ _).antisymm fun i hi ↦ let ⟨a, ha⟩ := ht i @@ -235,4 +239,7 @@ theorem sigma_diff_sigma : s₁.sigma t₁ \ s₂.sigma t₂ = s₁.sigma (t₁ ext fun x ↦ by by_cases h₁ : x.1 ∈ s₁ <;> by_cases h₂ : x.2 ∈ t₁ x.1 <;> simp [*, ← imp_iff_or_not] +lemma sigma_eq_biUnion : s.sigma t = ⋃ i ∈ s, Sigma.mk i '' t i := by + aesop + end Set diff --git a/Mathlib/Data/Setoid/Basic.lean b/Mathlib/Data/Setoid/Basic.lean index 621b1efe024e0f..a4cfd2c6db6bd1 100644 --- a/Mathlib/Data/Setoid/Basic.lean +++ b/Mathlib/Data/Setoid/Basic.lean @@ -306,7 +306,6 @@ theorem ker_iff_mem_preimage {f : α → β} {x y} : ker f x y ↔ x ∈ f ⁻¹ def liftEquiv (r : Setoid α) : { f : α → β // r ≤ ker f } ≃ (Quotient r → β) where toFun f := Quotient.lift (f : α → β) f.2 invFun f := ⟨f ∘ Quotient.mk'', fun x y h => by simp [ker_def, Quotient.sound' h]⟩ - left_inv := fun ⟨_, _⟩ => Subtype.eq <| funext fun _ => rfl right_inv _ := funext fun x => Quotient.inductionOn' x fun _ => rfl /-- The uniqueness part of the universal property for quotients of an arbitrary type. -/ @@ -427,7 +426,6 @@ def correspondence (r : Setoid α) : { s // r ≤ s } ≃o Setoid (Quotient r) w ⟨Quotient.ind s.1.2.1, fun {x y} ↦ Quotient.inductionOn₂ x y fun _ _ ↦ s.1.2.2, fun {x y z} ↦ Quotient.inductionOn₃ x y z fun _ _ _ ↦ s.1.2.3⟩⟩ invFun s := ⟨comap Quotient.mk' s, fun x y h => by rw [comap_rel, Quotient.eq'.2 h]⟩ - left_inv _ := rfl right_inv _ := ext fun x y ↦ Quotient.inductionOn₂ x y fun _ _ ↦ Iff.rfl map_rel_iff' := ⟨fun h x y hs ↦ @h ⟦x⟧ ⟦y⟧ hs, fun h x y ↦ Quotient.inductionOn₂ x y fun _ _ hs ↦ h hs⟩ diff --git a/Mathlib/Data/Sym/Basic.lean b/Mathlib/Data/Sym/Basic.lean index 474a8142c1e08c..52ed00875fbd90 100644 --- a/Mathlib/Data/Sym/Basic.lean +++ b/Mathlib/Data/Sym/Basic.lean @@ -431,8 +431,6 @@ The simp-normal form is for the `cast` to be pushed outward. -/ protected def cast {n m : ℕ} (h : n = m) : Sym α n ≃ Sym α m where toFun s := ⟨s.val, s.2.trans h⟩ invFun s := ⟨s.val, s.2.trans h.symm⟩ - left_inv _ := Subtype.ext rfl - right_inv _ := Subtype.ext rfl @[simp] theorem cast_rfl : Sym.cast rfl s = s := @@ -485,7 +483,6 @@ def oneEquiv : α ≃ Sym α 1 where fun ⟨_, _⟩ ⟨_, h⟩ ↦ fun perm ↦ by obtain ⟨a, rfl⟩ := List.length_eq_one_iff.mp h exact List.eq_of_mem_singleton (perm.mem_iff.mp <| List.head_mem _) - left_inv a := by rfl right_inv := by rintro ⟨⟨l⟩, h⟩; obtain ⟨a, rfl⟩ := List.length_eq_one_iff.mp h; rfl /-- Fill a term `m : Sym α (n - i)` with `i` copies of `a` to obtain a term of `Sym α n`. diff --git a/Mathlib/Data/Sym/Sym2.lean b/Mathlib/Data/Sym/Sym2.lean index 4a20070d8f4396..4ddf96ccdbae34 100644 --- a/Mathlib/Data/Sym/Sym2.lean +++ b/Mathlib/Data/Sym/Sym2.lean @@ -184,7 +184,6 @@ def lift : { f : α → α → β // ∀ a₁ a₂, f a₁ a₂ = f a₂ a₁ } rintro _ _ ⟨⟩ exacts [rfl, f.prop _ _] invFun F := ⟨curry (F ∘ Sym2.mk), fun _ _ => congr_arg F eq_swap⟩ - left_inv _ := Subtype.ext rfl right_inv _ := funext <| Sym2.ind fun _ _ => rfl @[simp] @@ -212,7 +211,6 @@ def lift₂ : ⟨fun a₁ a₂ b₁ b₂ => F s(a₁, a₂) s(b₁, b₂), fun a₁ a₂ b₁ b₂ => by constructor exacts [congr_arg₂ F eq_swap rfl, congr_arg₂ F rfl eq_swap]⟩ - left_inv _ := Subtype.ext rfl right_inv _ := funext₂ fun a b => Sym2.inductionOn₂ a b fun _ _ _ _ => rfl @[simp] diff --git a/Mathlib/Data/ZMod/Defs.lean b/Mathlib/Data/ZMod/Defs.lean index 04dafe14fc4d8b..97f9f5f3eea931 100644 --- a/Mathlib/Data/ZMod/Defs.lean +++ b/Mathlib/Data/ZMod/Defs.lean @@ -92,7 +92,7 @@ Commutative ring structure on `Fin n`. This is not a global instance, but can introduced locally using `open Fin.CommRing in ...`. This is not an instance because the `binop%` elaborator assumes that -htere are no non-trivial coercion loops, +there are no non-trivial coercion loops, but this instance would introduce a coercion from `Nat` to `Fin n` and back. Non-trivial loops lead to undesirable and counterintuitive elaboration behavior. diff --git a/Mathlib/Deprecated/RingHom.lean b/Mathlib/Deprecated/RingHom.lean new file mode 100644 index 00000000000000..cf2db2f5ee1c9d --- /dev/null +++ b/Mathlib/Deprecated/RingHom.lean @@ -0,0 +1,48 @@ +/- +Copyright (c) 2019 Amelia Livingston. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Amelia Livingston, Jireh Loreaux +-/ +import Mathlib.Algebra.Divisibility.Hom +import Mathlib.Algebra.Ring.Hom.Defs +import Mathlib.Data.Set.Insert + +/-! +# Additional lemmas about homomorphisms of semirings and rings + +These lemmas were in `Mathlib/Algebra/Hom/Ring/Defs.lean` and have now been deprecated. +-/ + +assert_not_exists RelIso Field + +open Function + +variable {α β : Type*} + +namespace RingHom + +section + +variable {_ : NonAssocSemiring α} {_ : NonAssocSemiring β} (f : α →+* β) + +/-- `f : α →+* β` has a trivial codomain iff its range is `{0}`. -/ +@[deprecated "Use range_eq_singleton_iff and codomain_trivial_iff_range_trivial" + (since := "2025-06-09") ] +theorem codomain_trivial_iff_range_eq_singleton_zero : (0 : β) = 1 ↔ Set.range f = {0} := + f.codomain_trivial_iff_range_trivial.trans + ⟨fun h => + Set.ext fun y => ⟨fun ⟨x, hx⟩ => by simp [← hx, h x], fun hy => ⟨0, by simpa using hy.symm⟩⟩, + fun h x => Set.mem_singleton_iff.mp (h ▸ Set.mem_range_self x)⟩ + +end + +section Semiring + +variable [Semiring α] [Semiring β] +@[deprecated map_dvd (since := "2025-06-09") ] +protected theorem map_dvd (f : α →+* β) {a b : α} : a ∣ b → f a ∣ f b := + map_dvd f + +end Semiring + +end RingHom diff --git a/Mathlib/FieldTheory/AxGrothendieck.lean b/Mathlib/FieldTheory/AxGrothendieck.lean index 64c6c5d44b25cf..bbfb25ebe78df4 100644 --- a/Mathlib/FieldTheory/AxGrothendieck.lean +++ b/Mathlib/FieldTheory/AxGrothendieck.lean @@ -145,7 +145,7 @@ theorem realize_genericPolyMapSurjOnOfInjOn (K ⊨ genericPolyMapSurjOnOfInjOn φ mons) ↔ ∀ (v : α → K) (p : { p : ι → MvPolynomial ι K // (∀ i, (p i).support ⊆ mons i) }), let f : (ι → K) → (ι → K) := fun v i => eval v (p.1 i) - let S : Set (ι → K) := fun x => φ.Realize (Sum.elim v x) + let S : Set (ι → K) := {x | φ.Realize (Sum.elim v x)} S.MapsTo f S → S.InjOn f → S.SurjOn f S := by classical have injOnAlt : ∀ {S : Set (ι → K)} (f : (ι → K) → (ι → K)), @@ -157,7 +157,7 @@ theorem realize_genericPolyMapSurjOnOfInjOn Finset.mem_univ, realize_bdEqual, Term.realize_relabel, true_imp_iff, Equiv.forall_congr_left (Equiv.curry (Fin 2) ι K), Equiv.curry_symm_apply, Function.uncurry, Fin.forall_fin_succ_pi, Fin.forall_fin_zero_pi, realize_iExs, realize_inf, Sum.forall_sum, - Set.MapsTo, Set.mem_def, injOnAlt, funext_iff, Set.SurjOn, Set.image, setOf, + Set.MapsTo, Set.mem_setOf_eq, injOnAlt, funext_iff, Set.SurjOn, Set.image, Set.subset_def, Equiv.forall_congr_left (mvPolynomialSupportLEEquiv mons)] simp +singlePass only [← Sum.elim_comp_inl_inr] -- was `simp` and very slow (https://github.com/leanprover-community/mathlib4/issues/19751) diff --git a/Mathlib/FieldTheory/Galois/Basic.lean b/Mathlib/FieldTheory/Galois/Basic.lean index e5a3abbd8f68b7..d85975e8db1630 100644 --- a/Mathlib/FieldTheory/Galois/Basic.lean +++ b/Mathlib/FieldTheory/Galois/Basic.lean @@ -109,8 +109,6 @@ theorem card_aut_eq_finrank [FiniteDimensional F E] [IsGalois F E] : let iso : F⟮α⟯ ≃ₐ[F] E := { toFun := fun e => e.val invFun := fun e => ⟨e, by rw [hα]; exact IntermediateField.mem_top⟩ - left_inv := fun _ => by ext; rfl - right_inv := fun _ => rfl map_mul' := fun _ _ => rfl map_add' := fun _ _ => rfl commutes' := fun _ => rfl } @@ -123,9 +121,7 @@ theorem card_aut_eq_finrank [FiniteDimensional F E] [IsGalois F E] : rw [← LinearEquiv.finrank_eq iso.toLinearEquiv] rw [← IntermediateField.AdjoinSimple.card_aut_eq_finrank F E H h_sep h_splits] apply Fintype.card_congr - apply Equiv.mk (fun ϕ => iso.trans (ϕ.trans iso.symm)) fun ϕ => iso.symm.trans (ϕ.trans iso) - · intro ϕ; ext1; simp only [trans_apply, apply_symm_apply] - · intro ϕ; ext1; simp only [trans_apply, symm_apply_apply] + exact Equiv.mk (fun ϕ => iso.trans (ϕ.trans iso.symm)) fun ϕ => iso.symm.trans (ϕ.trans iso) end IsGalois @@ -244,8 +240,6 @@ lemma fixedField_antitone : Antitone (@fixedField F _ E _ _) := def fixingSubgroupEquiv : fixingSubgroup K ≃* E ≃ₐ[K] E where toFun ϕ := { AlgEquiv.toRingEquiv (ϕ : E ≃ₐ[F] E) with commutes' := ϕ.mem } invFun ϕ := ⟨ϕ.restrictScalars _, ϕ.commutes⟩ - left_inv _ := by ext; rfl - right_inv _ := by ext; rfl map_mul' _ _ := by ext; rfl theorem fixingSubgroup_fixedField [FiniteDimensional F E] : fixingSubgroup (fixedField H) = H := by @@ -444,11 +438,7 @@ theorem of_card_aut_eq_finrank [FiniteDimensional F E] rw [← IntermediateField.finrank_eq_one_iff, ← mul_left_inj' (ne_of_lt p).symm, finrank_mul_finrank, ← h, one_mul, IntermediateField.finrank_fixedField_eq_card] apply Fintype.card_congr - exact - { toFun := fun g => ⟨g, Subgroup.mem_top g⟩ - invFun := (↑) - left_inv := fun g => rfl - right_inv := fun _ => by ext; rfl } + exact { toFun := fun g => ⟨g, Subgroup.mem_top g⟩, invFun := (↑) } variable {F} {E} variable {p : F[X]} diff --git a/Mathlib/FieldTheory/IntermediateField/Basic.lean b/Mathlib/FieldTheory/IntermediateField/Basic.lean index 7e54214dea960b..4b56b5a9014858 100644 --- a/Mathlib/FieldTheory/IntermediateField/Basic.lean +++ b/Mathlib/FieldTheory/IntermediateField/Basic.lean @@ -754,8 +754,6 @@ def extendScalars.orderIso : { E : Subfield L // F ≤ E } ≃o IntermediateField F L where toFun E := extendScalars E.2 invFun E := ⟨E.toSubfield, fun x hx ↦ E.algebraMap_mem ⟨x, hx⟩⟩ - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' {E E'} := by simp only [Equiv.coe_fn_mk] exact extendScalars_le_extendScalars_iff _ _ @@ -806,8 +804,6 @@ into an order isomorphism from def extendScalars.orderIso : { E : IntermediateField K L // F ≤ E } ≃o IntermediateField F L where toFun E := extendScalars E.2 invFun E := ⟨E.restrictScalars K, fun x hx ↦ E.algebraMap_mem ⟨x, hx⟩⟩ - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' {E E'} := by simp only [Equiv.coe_fn_mk] exact extendScalars_le_extendScalars_iff _ _ diff --git a/Mathlib/FieldTheory/IsAlgClosed/Basic.lean b/Mathlib/FieldTheory/IsAlgClosed/Basic.lean index fc4016d2d7aa92..3e66dc37f78709 100644 --- a/Mathlib/FieldTheory/IsAlgClosed/Basic.lean +++ b/Mathlib/FieldTheory/IsAlgClosed/Basic.lean @@ -467,8 +467,6 @@ def IntermediateField.algHomEquivAlgHomOfSplits (L : IntermediateField F A) invFun f := f.codRestrict _ fun x ↦ ((Algebra.IsIntegral.isIntegral x).map f).mem_intermediateField_of_minpoly_splits <| by rw [minpoly.algHom_eq f f.injective]; exact hL x - left_inv _ := rfl - right_inv _ := by rfl theorem IntermediateField.algHomEquivAlgHomOfSplits_apply_apply (L : IntermediateField F A) (hL : ∀ x : K, (minpoly F x).Splits (algebraMap F L)) (f : K →ₐ[F] L) (x : K) : diff --git a/Mathlib/FieldTheory/Minpoly/IsIntegrallyClosed.lean b/Mathlib/FieldTheory/Minpoly/IsIntegrallyClosed.lean index d1caf5c2a0ba9f..7bd10908640956 100644 --- a/Mathlib/FieldTheory/Minpoly/IsIntegrallyClosed.lean +++ b/Mathlib/FieldTheory/Minpoly/IsIntegrallyClosed.lean @@ -15,14 +15,14 @@ This file specializes the theory of minpoly to the case of an algebra over a GCD ## Main results * `minpoly.isIntegrallyClosed_eq_field_fractions`: For integrally closed domains, the minimal - polynomial over the ring is the same as the minimal polynomial over the fraction field. + polynomial over the ring is the same as the minimal polynomial over the fraction field. -* `minpoly.isIntegrallyClosed_dvd` : For integrally closed domains, the minimal polynomial divides - any primitive polynomial that has the integral element as root. +* `minpoly.isIntegrallyClosed_dvd`: For integrally closed domains, the minimal polynomial divides + any primitive polynomial that has the integral element as root. -* `IsIntegrallyClosed.Minpoly.unique` : The minimal polynomial of an element `x` is - uniquely characterized by its defining property: if there is another monic polynomial of minimal - degree that has `x` as a root, then this polynomial is equal to the minimal polynomial of `x`. +* `IsIntegrallyClosed.Minpoly.unique`: The minimal polynomial of an element `x` is uniquely + characterized by its defining property: if there is another monic polynomial of minimal degree + that has `x` as a root, then this polynomial is equal to the minimal polynomial of `x`. -/ diff --git a/Mathlib/FieldTheory/Normal/Closure.lean b/Mathlib/FieldTheory/Normal/Closure.lean index 681d6352d9d63e..fa0134a400521c 100644 --- a/Mathlib/FieldTheory/Normal/Closure.lean +++ b/Mathlib/FieldTheory/Normal/Closure.lean @@ -160,8 +160,6 @@ namespace normalClosure noncomputable def algHomEquiv : (K →ₐ[F] normalClosure F K L) ≃ (K →ₐ[F] L) where toFun := (normalClosure F K L).val.comp invFun f := f.codRestrict _ fun x ↦ f.fieldRange_le_normalClosure ⟨x, rfl⟩ - left_inv _ := rfl - right_inv _ := rfl @[stacks 0BMG "(1) normality."] instance normal [h : Normal F L] : Normal F (normalClosure F K L) := by diff --git a/Mathlib/FieldTheory/PerfectClosure.lean b/Mathlib/FieldTheory/PerfectClosure.lean index 7778f763faaa42..3fddc068639c46 100644 --- a/Mathlib/FieldTheory/PerfectClosure.lean +++ b/Mathlib/FieldTheory/PerfectClosure.lean @@ -440,7 +440,6 @@ noncomputable def lift (L : Type v) [CommSemiring L] [CharP L p] [PerfectRing L have := LeftInverse.iterate (frobeniusEquiv_symm_apply_frobenius L p) rw [iterate_add_apply, this _ _, add_comm n, iterate_add_apply, this _ _] } invFun f := f.comp (of K p) - left_inv f := by ext x; rfl right_inv f := by ext ⟨n, x⟩ simp only [quot_mk_eq_mk, RingHom.comp_apply, RingHom.coe_mk, MonoidHom.coe_mk, OneHom.coe_mk, diff --git a/Mathlib/FieldTheory/PrimitiveElement.lean b/Mathlib/FieldTheory/PrimitiveElement.lean index c3f355d4a81199..119a39c1df314b 100644 --- a/Mathlib/FieldTheory/PrimitiveElement.lean +++ b/Mathlib/FieldTheory/PrimitiveElement.lean @@ -341,13 +341,18 @@ end Field variable (F E : Type*) [Field F] [Field E] [Algebra F E] [FiniteDimensional F E] [Algebra.IsSeparable F E] +theorem AlgHom.natCard_of_splits (L : Type*) [Field L] [Algebra F L] + (hL : ∀ x : E, (minpoly F x).Splits (algebraMap F L)) : + Nat.card (E →ₐ[F] L) = finrank F E := + (AlgHom.natCard_of_powerBasis (L := L) (Field.powerBasisOfFiniteOfSeparable F E) + (Algebra.IsSeparable.isSeparable _ _) <| hL _).trans + (PowerBasis.finrank _).symm + @[simp] theorem AlgHom.card_of_splits (L : Type*) [Field L] [Algebra F L] (hL : ∀ x : E, (minpoly F x).Splits (algebraMap F L)) : Fintype.card (E →ₐ[F] L) = finrank F E := by - convert (AlgHom.card_of_powerBasis (L := L) (Field.powerBasisOfFiniteOfSeparable F E) - (Algebra.IsSeparable.isSeparable _ _) <| hL _).trans - (PowerBasis.finrank _).symm + rw [Fintype.card_eq_nat_card, AlgHom.natCard_of_splits F E L hL] @[simp] theorem AlgHom.card (K : Type*) [Field K] [IsAlgClosed K] [Algebra F K] : diff --git a/Mathlib/FieldTheory/RatFunc/Basic.lean b/Mathlib/FieldTheory/RatFunc/Basic.lean index 03f3d4fd927a44..3b3256dc23c552 100644 --- a/Mathlib/FieldTheory/RatFunc/Basic.lean +++ b/Mathlib/FieldTheory/RatFunc/Basic.lean @@ -224,8 +224,6 @@ This is an auxiliary definition; `simp`-normal form is `IsLocalization.algEquiv` def toFractionRingRingEquiv : RatFunc K ≃+* FractionRing K[X] where toFun := toFractionRing invFun := ofFractionRing - left_inv := fun ⟨_⟩ => rfl - right_inv _ := rfl map_add' := fun ⟨_⟩ ⟨_⟩ => by simp [← ofFractionRing_add] map_mul' := fun ⟨_⟩ ⟨_⟩ => by simp [← ofFractionRing_mul] diff --git a/Mathlib/FieldTheory/Separable.lean b/Mathlib/FieldTheory/Separable.lean index 60dbb22f3625b5..dfe790d2e9de25 100644 --- a/Mathlib/FieldTheory/Separable.lean +++ b/Mathlib/FieldTheory/Separable.lean @@ -755,13 +755,18 @@ variable {R S T : Type*} [CommRing S] variable {K L F : Type*} [Field K] [Field L] [Field F] variable [Algebra K S] [Algebra K L] -theorem AlgHom.card_of_powerBasis (pb : PowerBasis K S) (h_sep : IsSeparable K pb.gen) +theorem AlgHom.natCard_of_powerBasis (pb : PowerBasis K S) (h_sep : IsSeparable K pb.gen) (h_splits : (minpoly K pb.gen).Splits (algebraMap K L)) : - @Fintype.card (S →ₐ[K] L) (PowerBasis.AlgHom.fintype pb) = pb.dim := by + Nat.card (S →ₐ[K] L) = pb.dim := by classical - let _ := (PowerBasis.AlgHom.fintype pb : Fintype (S →ₐ[K] L)) - rw [Fintype.card_congr pb.liftEquiv', Fintype.card_of_subtype _ (fun x => Multiset.mem_toFinset), + rw [Nat.card_congr pb.liftEquiv', Nat.subtype_card _ (fun x => Multiset.mem_toFinset), ← pb.natDegree_minpoly, natDegree_eq_card_roots h_splits, Multiset.toFinset_card_of_nodup] exact nodup_roots ((separable_map (algebraMap K L)).mpr h_sep) +theorem AlgHom.card_of_powerBasis (pb : PowerBasis K S) (h_sep : IsSeparable K pb.gen) + (h_splits : (minpoly K pb.gen).Splits (algebraMap K L)) : + @Fintype.card (S →ₐ[K] L) (PowerBasis.AlgHom.fintype pb) = pb.dim := by + classical + rw [Fintype.card_eq_nat_card, AlgHom.natCard_of_powerBasis pb h_sep h_splits] + end CardAlgHom diff --git a/Mathlib/Geometry/Convex/Cone/Basic.lean b/Mathlib/Geometry/Convex/Cone/Basic.lean index db8f051ed9fa03..7ab99a31d90ac2 100644 --- a/Mathlib/Geometry/Convex/Cone/Basic.lean +++ b/Mathlib/Geometry/Convex/Cone/Basic.lean @@ -36,8 +36,7 @@ While `Convex R` is a predicate on sets, `ConvexCone R M` is a bundled convex co * [Emo Welzl and Bernd Gärtner, *Cone Programming*][welzl_garter] -/ - -assert_not_exists NormedSpace Real Cardinal +assert_not_exists TopologicalSpace Real Cardinal open Set LinearMap Pointwise diff --git a/Mathlib/Analysis/Convex/Cone/Pointed.lean b/Mathlib/Geometry/Convex/Cone/Pointed.lean similarity index 89% rename from Mathlib/Analysis/Convex/Cone/Pointed.lean rename to Mathlib/Geometry/Convex/Cone/Pointed.lean index 41be256ffc35ac..e74a11f540ad41 100644 --- a/Mathlib/Analysis/Convex/Cone/Pointed.lean +++ b/Mathlib/Geometry/Convex/Cone/Pointed.lean @@ -3,9 +3,8 @@ Copyright (c) 2023 Apurva Nakade. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Apurva Nakade -/ -import Mathlib.Analysis.Convex.Cone.InnerDual import Mathlib.Algebra.Order.Nonneg.Module -import Mathlib.Algebra.Module.Submodule.Basic +import Mathlib.Geometry.Convex.Cone.Basic /-! # Pointed cones @@ -17,6 +16,8 @@ as it allows us to use the `Module` API to work with convex cones. -/ +assert_not_exists TopologicalSpace + variable {𝕜 E F G : Type*} local notation3 "𝕜≥0" => {c : 𝕜 // 0 ≤ c} @@ -175,23 +176,4 @@ theorem toConvexCone_positive : ↑(positive 𝕜 E) = ConvexCone.positive 𝕜 rfl end PositiveCone -section Dual - -variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℝ E] - -/-- The inner dual cone of a pointed cone is a pointed cone. -/ -def dual (S : PointedCone ℝ E) : PointedCone ℝ E := - ((S : Set E).innerDualCone).toPointedCone <| pointed_innerDualCone (S : Set E) - -@[simp, norm_cast] -theorem toConvexCone_dual (S : PointedCone ℝ E) : ↑(dual S) = (S : Set E).innerDualCone := - rfl - -open scoped InnerProductSpace in -@[simp] -theorem mem_dual {S : PointedCone ℝ E} {y : E} : y ∈ dual S ↔ ∀ ⦃x⦄, x ∈ S → 0 ≤ ⟪x, y⟫_ℝ := by - rfl - -end Dual - end PointedCone diff --git a/Mathlib/Geometry/Convex/Cone/README.md b/Mathlib/Geometry/Convex/Cone/README.md index 5945281bab117f..f3800c0928a531 100644 --- a/Mathlib/Geometry/Convex/Cone/README.md +++ b/Mathlib/Geometry/Convex/Cone/README.md @@ -1,10 +1,12 @@ -# Theory of convex cones in topological modules +# Algebraic theory of convex cones -This subfolder is destined to contain results about convex cones that do not require a norm nor an inner product. +This subfolder is destined to contain results about convex cones that do not require a topology, +a norm nor an inner product. -See the `Mathlib.Analysis.Convex.Cone` folder for the results that need a norm or an inner product. +See the `Mathlib.Analysis.Convex.Cone` folder for the topological results. ## Topics The convex cone topics currently covered are: * Convex cones +* Pointed cones diff --git a/Mathlib/Geometry/Euclidean/Altitude.lean b/Mathlib/Geometry/Euclidean/Altitude.lean index db4483900b05cc..d5befcc0c18151 100644 --- a/Mathlib/Geometry/Euclidean/Altitude.lean +++ b/Mathlib/Geometry/Euclidean/Altitude.lean @@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Joseph Myers -/ import Mathlib.Geometry.Euclidean.Projection +import Mathlib.Analysis.InnerProductSpace.Affine /-! # Altitudes of a simplex @@ -140,6 +141,11 @@ def altitudeFoot {n : ℕ} [NeZero n] (s : Simplex ℝ P n) (i : Fin (n + 1)) : rw [eq_comm, altitudeFoot, orthogonalProjectionSpan, orthogonalProjection_eq_self_iff] at h simp at h +@[simp] lemma altitudeFoot_mem_affineSpan_image_compl {n : ℕ} [NeZero n] (s : Simplex ℝ P n) + (i : Fin (n + 1)) : s.altitudeFoot i ∈ affineSpan ℝ (s.points '' {i}ᶜ) := by + rw [← range_faceOpposite_points] + exact orthogonalProjection_mem _ + lemma altitudeFoot_mem_affineSpan_faceOpposite {n : ℕ} [NeZero n] (s : Simplex ℝ P n) (i : Fin (n + 1)) : s.altitudeFoot i ∈ affineSpan ℝ (Set.range (s.faceOpposite i).points) := orthogonalProjection_mem _ @@ -168,6 +174,7 @@ from that vertex. -/ def height {n : ℕ} [NeZero n] (s : Simplex ℝ P n) (i : Fin (n + 1)) : ℝ := dist (s.points i) (s.altitudeFoot i) +@[simp] lemma height_pos {n : ℕ} [NeZero n] (s : Simplex ℝ P n) (i : Fin (n + 1)) : 0 < s.height i := by simp [height] @@ -184,6 +191,108 @@ def evalHeight : PositivityExt where eval {u α} _ _ e := do example {n : ℕ} [NeZero n] (s : Simplex ℝ P n) (i : Fin (n + 1)) : 0 < s.height i := by positivity +open scoped RealInnerProductSpace + +variable {n : ℕ} [NeZero n] (s : Simplex ℝ P n) + +/-- The inner product of an edge from `j` to `i` and the vector from the foot of `i` to `i` +is the square of the height. -/ +lemma inner_vsub_vsub_altitudeFoot_eq_height_sq {i j : Fin (n + 1)} (h : i ≠ j) : + ⟪s.points i -ᵥ s.points j, s.points i -ᵥ s.altitudeFoot i⟫ = s.height i ^ 2 := by + suffices ⟪s.points j -ᵥ s.altitudeFoot i, s.points i -ᵥ s.altitudeFoot i⟫ = 0 by + rwa [height, inner_vsub_vsub_left_eq_dist_sq_right_iff, inner_vsub_left_eq_zero_symm] + refine Submodule.inner_right_of_mem_orthogonal + (K := vectorSpan ℝ (s.points '' {i}ᶜ)) + (vsub_mem_vectorSpan_of_mem_affineSpan_of_mem_affineSpan + (s.mem_affineSpan_image_iff.2 h.symm) + (altitudeFoot_mem_affineSpan_image_compl _ _)) + ?_ + rw [← direction_affineSpan, ← range_faceOpposite_points] + exact vsub_orthogonalProjection_mem_direction_orthogonal _ _ + +/-- +The inner product of two distinct altitudes has absolute value strictly less than the product of +their lengths. + +Equivalently, neither vector is a multiple of the other; the angle between them is not 0 or π. -/ +lemma abs_inner_vsub_altitudeFoot_lt_mul {i j : Fin (n + 1)} (hij : i ≠ j) (hn : 1 < n) : + |⟪s.points i -ᵥ s.altitudeFoot i, s.points j -ᵥ s.altitudeFoot j⟫| + < s.height i * s.height j := by + apply LE.le.lt_of_ne + · convert abs_real_inner_le_norm _ _ using 1 + simp only [dist_eq_norm_vsub, abs_eq_self, height] + · simp_rw [height, dist_eq_norm_vsub] + rw [← Real.norm_eq_abs, ne_eq, norm_inner_eq_norm_iff (by simp) (by simp)] + rintro ⟨r, hr, h⟩ + suffices s.points j -ᵥ s.altitudeFoot j = 0 by + simp at this + rw [← Submodule.mem_bot ℝ, + ← Submodule.inf_orthogonal_eq_bot (vectorSpan ℝ (Set.range s.points))] + refine ⟨vsub_mem_vectorSpan_of_mem_affineSpan_of_mem_affineSpan + (mem_affineSpan _ (Set.mem_range_self _)) ?_, ?_⟩ + · refine SetLike.le_def.1 (affineSpan_mono _ ?_) (Subtype.property _) + simp + · rw [SetLike.mem_coe] + have hk : ∃ k, k ≠ i ∧ k ≠ j := Fin.exists_ne_and_ne_of_two_lt i j (by linarith only [hn]) + have hs : vectorSpan ℝ (Set.range s.points) = + vectorSpan ℝ (Set.range (s.faceOpposite i).points) ⊔ + vectorSpan ℝ (Set.range (s.faceOpposite j).points) := by + rcases hk with ⟨k, hki, hkj⟩ + have hki' : s.points k ∈ Set.range (s.faceOpposite i).points := by + rw [range_faceOpposite_points] + exact Set.mem_image_of_mem _ hki + have hkj' : s.points k ∈ Set.range (s.faceOpposite j).points := by + rw [range_faceOpposite_points] + exact Set.mem_image_of_mem _ hkj + have hs : + Set.range s.points = + Set.range (s.faceOpposite i).points ∪ Set.range (s.faceOpposite j).points := by + simp only [range_faceOpposite_points, ← Set.image_union] + simp_rw [← Set.image_univ, ← Set.compl_inter] + rw [Set.inter_singleton_eq_empty.mpr ?_, Set.compl_empty] + simpa using hij.symm + convert AffineSubspace.vectorSpan_union_of_mem_of_mem ℝ hki' hkj' + rw [hs, ← Submodule.inf_orthogonal, Submodule.mem_inf] + refine ⟨?_, ?_⟩ + · rw [h, ← direction_affineSpan] + exact Submodule.smul_mem _ _ (vsub_orthogonalProjection_mem_direction_orthogonal _ _) + · rw [← direction_affineSpan] + exact vsub_orthogonalProjection_mem_direction_orthogonal _ _ + +/-- +The inner product of two altitudes has value strictly greater than the negated product of +their lengths. +-/ +lemma neg_mul_lt_inner_vsub_altitudeFoot (i j : Fin (n + 1)) (hn : 1 < n) : + -(s.height i * s.height j) + < ⟪s.points i -ᵥ s.altitudeFoot i, s.points j -ᵥ s.altitudeFoot j⟫ := by + obtain rfl | hij := eq_or_ne i j + · rw [real_inner_self_eq_norm_sq] + refine lt_of_lt_of_le (b := 0) ?_ ?_ + · rw [neg_lt_zero] + positivity + · positivity + rw [neg_lt] + refine lt_of_abs_lt ?_ + rw [abs_neg] + exact abs_inner_vsub_altitudeFoot_lt_mul s hij hn + +lemma abs_inner_vsub_altitudeFoot_div_lt_one {i j : Fin (n + 1)} (hij : i ≠ j) (hn : 1 < n) : + |⟪s.points i -ᵥ s.altitudeFoot i, s.points j -ᵥ s.altitudeFoot j⟫ + / (s.height i * s.height j)| < 1 := by + rw [abs_div, div_lt_one (by simp [height])] + nth_rw 2 [abs_eq_self.2] + · exact abs_inner_vsub_altitudeFoot_lt_mul _ hij hn + · simp only [height] + positivity + +lemma neg_one_lt_inner_vsub_altitudeFoot_div + {n : ℕ} [NeZero n] (s : Simplex ℝ P n) (i j : Fin (n + 1)) (hn : 1 < n) : + -1 < ⟪s.points i -ᵥ s.altitudeFoot i, s.points j -ᵥ s.altitudeFoot j⟫ + / (s.height i * s.height j) := by + rw [neg_lt, neg_div', div_lt_one (by simp [height]), neg_lt] + exact neg_mul_lt_inner_vsub_altitudeFoot _ _ _ hn + end Simplex end Affine diff --git a/Mathlib/Geometry/Euclidean/Incenter.lean b/Mathlib/Geometry/Euclidean/Incenter.lean new file mode 100644 index 00000000000000..5c9f6c50afeaef --- /dev/null +++ b/Mathlib/Geometry/Euclidean/Incenter.lean @@ -0,0 +1,431 @@ +/- +Copyright (c) 2025 Joseph Myers. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joseph Myers +-/ +import Mathlib.Geometry.Euclidean.Altitude +import Mathlib.Geometry.Euclidean.SignedDist +import Mathlib.Geometry.Euclidean.Sphere.Basic +import Mathlib.Tactic.Positivity.Finset + +/-! +# Incenters and excenters of simplices. + +This file defines the insphere and exspheres of a simplex (tangent to the faces of the simplex), +and the center and radius of such spheres. + +## Main definitions + +* `Affine.Simplex.ExcenterExists` says whether an excenter exists with a given set of indices + (that determine, up to negating all the signs, which vertices of the simplex lie on the same + side of the opposite face as the excenter and which lie on the opposite side of that face). +* `Affine.Simplex.excenterWeights` are the weights of the excenter with the given set of + indices, if it exists, as an affine combination of the vertices. +* `Affine.Simplex.exsphere` is the exsphere with the given set of indices, if it exists, with + shorthands: + * `Affine.Simplex.excenter` for the center of this sphere + * `Affine.Simplex.exradius` for the radius of this sphere +* `Affine.Simplex.insphere` is the insphere, with shorthands: + * `Affine.Simplex.incenter` for the center of this sphere + * `Affine.Simplex.inradius` for the radius of this sphere + +## References + +* https://en.wikipedia.org/wiki/Incircle_and_excircles +* https://en.wikipedia.org/wiki/Incenter + +-/ + + +open EuclideanGeometry +open scoped Finset RealInnerProductSpace + +variable {V P : Type*} [NormedAddCommGroup V] [InnerProductSpace ℝ V] [MetricSpace P] +variable [NormedAddTorsor V P] + +noncomputable section + +namespace Affine + +namespace Simplex + +variable {n : ℕ} [NeZero n] (s : Simplex ℝ P n) + +/-- The unnormalized weights of the vertices in an affine combination that gives an excenter with +signs determined by the given set of indices (for the empty set, this is the incenter; for a +singleton set, this is the excenter opposite a vertex). An excenter with those signs exists if +and only if the sum of these weights is nonzero (so the normalized weights sum to 1). -/ +def excenterWeightsUnnorm (signs : Finset (Fin (n + 1))) (i : Fin (n + 1)) : ℝ := + (if i ∈ signs then -1 else 1) * (s.height i)⁻¹ + +@[simp] lemma excenterWeightsUnnorm_empty_apply (i : Fin (n + 1)) : + s.excenterWeightsUnnorm ∅ i = (s.height i)⁻¹ := + one_mul _ + +/-- Whether an excenter exists with a given choice of signs. -/ +def ExcenterExists (signs : Finset (Fin (n + 1))) : Prop := + ∑ i, s.excenterWeightsUnnorm signs i ≠ 0 + +/-- The normalized weights of the vertices in an affine combination that gives an excenter with +signs determined by the given set of indices. An excenter with those signs exists if and only if +the sum of these weights is 1. -/ +def excenterWeights (signs : Finset (Fin (n + 1))) : Fin (n + 1) → ℝ := + (∑ i, s.excenterWeightsUnnorm signs i)⁻¹ • s.excenterWeightsUnnorm signs + +@[simp] lemma excenterWeightsUnnorm_compl (signs : Finset (Fin (n + 1))) : + s.excenterWeightsUnnorm signsᶜ = -s.excenterWeightsUnnorm signs := by + ext i + by_cases h : i ∈ signs <;> simp [excenterWeightsUnnorm, h] + +@[simp] lemma excenterWeights_compl (signs : Finset (Fin (n + 1))) : + s.excenterWeights signsᶜ = s.excenterWeights signs := by + simp [excenterWeights, inv_neg] + +@[simp] lemma excenterExists_compl {signs : Finset (Fin (n + 1))} : + s.ExcenterExists signsᶜ ↔ s.ExcenterExists signs := by + simp [ExcenterExists] + +lemma sum_excenterWeights (signs : Finset (Fin (n + 1))) [Decidable (s.ExcenterExists signs)] : + ∑ i, s.excenterWeights signs i = if s.ExcenterExists signs then 1 else 0 := by + simp_rw [ExcenterExists, excenterWeights] + split_ifs with h + · simp [← Finset.mul_sum, h] + · simp only [ne_eq, not_not] at h + simp [h] + +@[simp] lemma sum_excenterWeights_eq_one_iff {signs : Finset (Fin (n + 1))} : + ∑ i, s.excenterWeights signs i = 1 ↔ s.ExcenterExists signs := by + classical + simp [sum_excenterWeights] + +alias ⟨_, ExcenterExists.sum_excenterWeights_eq_one⟩ := sum_excenterWeights_eq_one_iff + +lemma sum_excenterWeightsUnnorm_empty_pos : 0 < ∑ i, s.excenterWeightsUnnorm ∅ i := by + simp_rw [excenterWeightsUnnorm_empty_apply] + positivity + +/-- The existence of the incenter, expressed in terms of `ExcenterExists`. -/ +@[simp] lemma excenterExists_empty : s.ExcenterExists ∅ := + s.sum_excenterWeightsUnnorm_empty_pos.ne' + +lemma sum_inv_height_sq_smul_vsub_eq_zero : + ∑ i, (s.height i)⁻¹ ^ 2 • (s.points i -ᵥ s.altitudeFoot i) = 0 := by + suffices ∀ i, i ≠ 0 → + ∑ j, ⟪s.points i -ᵥ s.points 0, (s.height j)⁻¹ ^ 2 • (s.points j -ᵥ s.altitudeFoot j)⟫ = 0 by + rw [← Submodule.mem_bot ℝ, + ← Submodule.inf_orthogonal_eq_bot (vectorSpan ℝ (Set.range s.points))] + refine ⟨Submodule.sum_smul_mem _ _ fun i hi ↦ + vsub_mem_vectorSpan_of_mem_affineSpan_of_mem_affineSpan + (mem_affineSpan _ (Set.mem_range_self _)) + (altitudeFoot_mem_affineSpan _ _), + ?_⟩ + rw [vectorSpan_range_eq_span_range_vsub_right_ne _ _ 0, Submodule.span_range_eq_iSup, + ← Submodule.iInf_orthogonal, Submodule.iInf_coe, Set.mem_iInter] + intro i + rcases i with ⟨i, hi⟩ + simpa only [SetLike.mem_coe, Submodule.mem_orthogonal_singleton_iff_inner_right, inner_sum] + using this i hi + intro i hi + rw [← Finset.add_sum_erase _ _ (Finset.mem_univ 0), + ← Finset.add_sum_erase _ _ (Finset.mem_erase.2 ⟨hi, Finset.mem_univ _⟩), ← add_assoc] + convert add_zero _ + · convert Finset.sum_const_zero with j hj + rw [real_inner_smul_right] + convert mul_zero _ + rw [← Submodule.mem_orthogonal_singleton_iff_inner_right] + refine SetLike.le_def.1 (Submodule.orthogonal_le ?_) + (vsub_orthogonalProjection_mem_direction_orthogonal _ _) + rw [Submodule.span_singleton_le_iff_mem, direction_affineSpan] + simp only [Finset.mem_erase, Finset.mem_univ, and_true] at hj + refine vsub_mem_vectorSpan _ ?_ ?_ <;> + simp only [range_faceOpposite_points, Set.mem_image, Set.mem_setOf_eq] + · exact ⟨i, hj.1.symm, rfl⟩ + · exact ⟨0, hj.2.symm, rfl⟩ + · rw [inner_smul_right, inner_smul_right, inner_vsub_vsub_altitudeFoot_eq_height_sq _ hi, + ← neg_vsub_eq_vsub_rev, inner_neg_left, inner_vsub_vsub_altitudeFoot_eq_height_sq _ hi.symm, + mul_neg, inv_pow] + simp [height] + +/-- The inverse of the distance from one vertex to the opposite face, expressed as a sum of +multiples of that quantity for the other vertices. The multipliers, expressed here in terms of +inner products, are equal to the cosines of angles between faces (informally, the inverse +distances are proportional to the volumes of the faces and this is equivalent to expressing +the volume of a face as the sum of the signed volumes of projections of the other faces onto that +face). -/ +lemma inv_height_eq_sum_mul_inv_dist (i : Fin (n + 1)) : + (s.height i)⁻¹ = + ∑ j ∈ {k | k ≠ i}, + -(⟪s.points i -ᵥ s.altitudeFoot i, s.points j -ᵥ s.altitudeFoot j⟫ / + (s.height i * s.height j)) * + (s.height j)⁻¹ := by + rw [← sub_eq_zero] + simp_rw [neg_mul] + rw [Finset.sum_neg_distrib, sub_neg_eq_add, Finset.filter_ne', + Finset.sum_erase_eq_sub (Finset.mem_univ _), real_inner_self_eq_norm_mul_norm, + ← dist_eq_norm_vsub] + simp only [height, ne_eq, mul_eq_zero, dist_eq_zero, ne_altitudeFoot, or_self, + not_false_eq_true, div_self, one_mul, add_sub_cancel] + have h := s.sum_inv_height_sq_smul_vsub_eq_zero + apply_fun fun v ↦ (s.height i)⁻¹ * ⟪s.points i -ᵥ s.altitudeFoot i, v⟫ at h + rw [inner_sum, Finset.mul_sum] at h + simp only [inner_zero_right, mul_zero, inner_smul_right, height] at h + convert h using 2 with j + ring + +/-- The inverse of the distance from one vertex to the opposite face is less than the sum of that +quantity for the other vertices. This implies the existence of the excenter opposite that vertex; +it also implies that the image of the incenter under a homothety with scale factor 2 about a +vertex lies outside the simplex. -/ +lemma inv_height_lt_sum_inv_height (hn : 1 < n) (i : Fin (n + 1)) : + (s.height i)⁻¹ < ∑ j ∈ {k | k ≠ i}, (s.height j)⁻¹ := by + rw [inv_height_eq_sum_mul_inv_dist] + refine Finset.sum_lt_sum_of_nonempty ?_ ?_ + · rw [Finset.filter_ne', ← Finset.card_ne_zero] + simp only [Finset.mem_univ, Finset.card_erase_of_mem, Finset.card_univ, Fintype.card_fin, + add_tsub_cancel_right] + exact NeZero.ne _ + · rintro j hj + refine mul_lt_of_lt_one_left ?_ ?_ + · simp [height_pos] + · rw [neg_lt] + exact neg_one_lt_inner_vsub_altitudeFoot_div _ _ _ hn + +lemma sum_excenterWeightsUnnorm_singleton_pos (hn : 1 < n) (i : Fin (n + 1)) : + 0 < ∑ j, s.excenterWeightsUnnorm {i} j := by + rw [← Finset.sum_add_sum_compl {i}, Finset.sum_singleton] + nth_rw 1 [excenterWeightsUnnorm] + simp only [Finset.mem_singleton, ↓reduceIte, neg_mul, one_mul, lt_neg_add_iff_add_lt, add_zero] + convert s.inv_height_lt_sum_inv_height hn i using 2 with j h + · ext j + simp + · simp only [ne_eq, Finset.mem_filter, Finset.mem_univ, true_and] at h + simp [excenterWeightsUnnorm, h] + +/-- The existence of the excenter opposite a vertex (in two or more dimensions), expressed in +terms of `ExcenterExists`. -/ +lemma excenterExists_singleton (hn : 1 < n) (i : Fin (n + 1)) : s.ExcenterExists {i} := + (s.sum_excenterWeightsUnnorm_singleton_pos hn i).ne' + +/-- The exsphere with signs determined by the given set of indices (for the empty set, this is +the insphere; for a singleton set, this is the exsphere opposite a vertex). This is only +meaningful if `s.ExcenterExists`; otherwise, it is a sphere of radius zero at some arbitrary +point. -/ +def exsphere (signs : Finset (Fin (n + 1))) : Sphere P where + center := Finset.univ.affineCombination ℝ s.points (s.excenterWeights signs) + radius := |(∑ i, s.excenterWeightsUnnorm signs i)⁻¹| + +/-- The insphere of a simplex. -/ +def insphere : Sphere P := + s.exsphere ∅ + +/-- The excenter with signs determined by the given set of indices (for the empty set, this is +the incenter; for a singleton set, this is the excenter opposite a vertex). This is only +meaningful if `s.ExcenterExists signs`; otherwise, it is some arbitrary point. -/ +def excenter (signs : Finset (Fin (n + 1))) : P := + (s.exsphere signs).center + +/-- The incenter of a simplex. -/ +def incenter : P := + (s.exsphere ∅).center + +/-- The distance between an excenter and a face of the simplex (zero if no such excenter +exists). -/ +def exradius (signs : Finset (Fin (n + 1))) : ℝ := + (s.exsphere signs).radius + +/-- The distance between the incenter and a face of the simplex. -/ +def inradius : ℝ := + (s.exsphere ∅).radius + +@[simp] lemma exsphere_center (signs : Finset (Fin (n + 1))) : + (s.exsphere signs).center = s.excenter signs := + rfl + +@[simp] lemma exsphere_radius (signs : Finset (Fin (n + 1))) : + (s.exsphere signs).radius = s.exradius signs := + rfl + +@[simp] lemma insphere_center : s.insphere.center = s.incenter := + rfl + +@[simp] lemma insphere_radius : s.insphere.radius = s.inradius := + rfl + +@[simp] lemma exsphere_empty : s.exsphere ∅ = s.insphere := + rfl + +@[simp] lemma excenter_empty : s.excenter ∅ = s.incenter := + rfl + +@[simp] lemma exradius_empty : s.exradius ∅ = s.inradius := + rfl + +@[simp] lemma exsphere_univ : s.exsphere Finset.univ = s.insphere := by + rw [exsphere, ← Finset.compl_empty, excenterWeightsUnnorm_compl, excenterWeights_compl] + simp only [Finset.mem_univ, Pi.neg_apply, Finset.sum_neg_distrib, inv_neg, abs_neg] + rfl + +@[simp] lemma excenter_univ : s.excenter Finset.univ = s.incenter := by + rw [excenter, exsphere_univ, insphere_center] + +@[simp] lemma exradius_univ : s.exradius Finset.univ = s.inradius := by + rw [exradius, exsphere_univ, insphere_radius] + +lemma excenter_eq_affineCombination (signs : Finset (Fin (n + 1))) : + s.excenter signs = Finset.univ.affineCombination ℝ s.points (s.excenterWeights signs) := + rfl + +lemma exradius_eq_abs_inv_sum (signs : Finset (Fin (n + 1))) : + s.exradius signs = |(∑ i, s.excenterWeightsUnnorm signs i)⁻¹| := + rfl + +lemma incenter_eq_affineCombination : + s.incenter = Finset.univ.affineCombination ℝ s.points (s.excenterWeights ∅) := + rfl + +lemma inradius_eq_abs_inv_sum : s.inradius = |(∑ i, s.excenterWeightsUnnorm ∅ i)⁻¹| := + rfl + +lemma exradius_nonneg (signs : Finset (Fin (n + 1))) : 0 ≤ s.exradius signs := + abs_nonneg _ + +variable {s} in +lemma ExcenterExists.exradius_pos {signs : Finset (Fin (n + 1))} (h : s.ExcenterExists signs) : + 0 < s.exradius signs := + abs_pos.2 (inv_ne_zero h) + +lemma inradius_pos : 0 < s.inradius := + s.excenterExists_empty.exradius_pos + +lemma exradius_singleton_pos (hn : 1 < n) (i : Fin (n + 1)) : 0 < s.exradius {i} := + (s.excenterExists_singleton hn i).exradius_pos + +variable {s} in +lemma ExcenterExists.excenter_mem_affineSpan_range {signs : Finset (Fin (n + 1))} + (h : s.ExcenterExists signs) : s.excenter signs ∈ affineSpan ℝ (Set.range s.points) := + affineCombination_mem_affineSpan h.sum_excenterWeights_eq_one _ + +lemma incenter_mem_affineSpan_range : s.incenter ∈ affineSpan ℝ (Set.range s.points) := + s.excenterExists_empty.excenter_mem_affineSpan_range + +lemma excenter_singleton_mem_affineSpan_range (hn : 1 < n) (i : Fin (n + 1)) : + s.excenter {i} ∈ affineSpan ℝ (Set.range s.points) := + (s.excenterExists_singleton hn i).excenter_mem_affineSpan_range + +variable {s} in +lemma ExcenterExists.signedInfDist_excenter_eq_mul_sum_inv {signs : Finset (Fin (n + 1))} + (h : s.ExcenterExists signs) (i : Fin (n + 1)) : + s.signedInfDist i (s.excenter signs) = + (if i ∈ signs then -1 else 1) * (∑ j, s.excenterWeightsUnnorm signs j)⁻¹ := by + simp_rw [excenter_eq_affineCombination, + signedInfDist_affineCombination _ _ h.sum_excenterWeights_eq_one, excenterWeights, + Pi.smul_apply, ← dist_eq_norm_vsub, excenterWeightsUnnorm] + rw [← altitudeFoot, ← height] + simp [mul_assoc, (s.height_pos i).ne'] + +variable {s} in +/-- The signed distance between the excenter and its projection in the plane of each face is the +exradius. -/ +lemma ExcenterExists.signedInfDist_excenter {signs : Finset (Fin (n + 1))} + (h : s.ExcenterExists signs) (i : Fin (n + 1)) : + s.signedInfDist i (s.excenter signs) = (if i ∈ signs then -1 else 1) * + SignType.sign (∑ j, s.excenterWeightsUnnorm signs j) * (s.exradius signs) := by + rw [h.signedInfDist_excenter_eq_mul_sum_inv, mul_assoc, exradius_eq_abs_inv_sum] + congr + rw [← mul_eq_one_iff_inv_eq₀ h, ← mul_assoc, self_mul_sign, ← abs_mul, mul_inv_cancel₀ h, abs_one] + +/-- The signed distance between the incenter and its projection in the plane of each face is the +inradius. + +In other words, the incenter is _internally_ tangent to the faces. -/ +lemma signedInfDist_incenter (i : Fin (n + 1)) : s.signedInfDist i s.incenter = s.inradius := by + rw [incenter, exsphere_center, s.excenterExists_empty.signedInfDist_excenter] + simp (discharger := positivity) + +variable {s} in +/-- The distance between the excenter and its projection in the plane of each face is the exradius. + +In other words, the exsphere is tangent to the faces. -/ +lemma ExcenterExists.dist_excenter {signs : Finset (Fin (n + 1))} (h : s.ExcenterExists signs) + (i : Fin (n + 1)) : + dist (s.excenter signs) ((s.faceOpposite i).orthogonalProjectionSpan (s.excenter signs)) = + s.exradius signs := by + rw [← abs_signedInfDist_eq_dist_of_mem_affineSpan_range i h.excenter_mem_affineSpan_range, + h.signedInfDist_excenter, abs_mul, abs_mul, abs_of_nonneg (s.exradius_nonneg signs)] + simp only [abs_ite, abs_neg, abs_one, ite_self, one_mul] + rcases lt_trichotomy 0 (∑ i, s.excenterWeightsUnnorm signs i) with h' | h' | h' + · simp [h'] + · simp [h h'.symm] + · simp [h'] + +/-- The distance between the incenter and its projection in the plane of each face is the inradius. + +In other words, the incenter is tangent to the faces. -/ +lemma dist_incenter (i : Fin (n + 1)) : + dist s.incenter ((s.faceOpposite i).orthogonalProjectionSpan s.incenter) = s.inradius := + s.excenterExists_empty.dist_excenter _ + +lemma exists_forall_signedInfDist_eq_iff_excenterExists_and_eq_excenter {p : P} + (hp : p ∈ affineSpan ℝ (Set.range s.points)) {signs : Finset (Fin (n + 1))} : + (∃ r : ℝ, ∀ i, s.signedInfDist i p = (if i ∈ signs then -1 else 1) * r) ↔ + s.ExcenterExists signs ∧ p = s.excenter signs := by + refine ⟨?_, ?_⟩ + · rintro ⟨r, h⟩ + obtain ⟨w, h1, rfl⟩ := eq_affineCombination_of_mem_affineSpan_of_fintype hp + have h' : ∀ i, w i * ‖s.points i -ᵥ s.altitudeFoot i‖ = (if i ∈ signs then -1 else 1) * r := by + intro i + rw [altitudeFoot, ← s.signedInfDist_affineCombination i h1] + exact h i + simp_rw [← dist_eq_norm_vsub] at h' + have h'' : ∀ i, w i = r * s.excenterWeightsUnnorm signs i := by + simp_rw [excenterWeightsUnnorm] + intro i + replace h' := h' i + rw [← height, ← eq_div_iff (s.height_pos i).ne'] at h' + rw [h', mul_comm, div_eq_mul_inv, mul_assoc, height, altitudeFoot, orthogonalProjectionSpan] + have hw : w = s.excenterWeights signs := by + simp_rw [h'', ← Finset.mul_sum] at h1 + ext j + rw [h'', eq_inv_of_mul_eq_one_left h1] + rfl + subst hw + exact ⟨s.sum_excenterWeights_eq_one_iff.1 h1, rfl⟩ + · rintro ⟨h, rfl⟩ + refine ⟨SignType.sign (∑ j, s.excenterWeightsUnnorm signs j) * (s.exradius signs), fun i ↦ ?_⟩ + rw [h.signedInfDist_excenter] + simp + +lemma exists_forall_signedInfDist_eq_iff_eq_incenter {p : P} + (hp : p ∈ affineSpan ℝ (Set.range s.points)) : + (∃ r : ℝ, ∀ i, s.signedInfDist i p = r) ↔ p = s.incenter := by + convert s.exists_forall_signedInfDist_eq_iff_excenterExists_and_eq_excenter hp (signs := ∅) + · simp + · simp [excenterExists_empty] + +lemma exists_forall_dist_eq_iff_exists_excenterExists_and_eq_excenter {p : P} + (hp : p ∈ affineSpan ℝ (Set.range s.points)) : + (∃ r : ℝ, ∀ i, dist p ((s.faceOpposite i).orthogonalProjectionSpan p) = r) ↔ + ∃ signs, s.ExcenterExists signs ∧ p = s.excenter signs := by + simp_rw [← abs_signedInfDist_eq_dist_of_mem_affineSpan_range _ hp] + refine ⟨?_, ?_⟩ + · rintro ⟨r, h⟩ + have h' : ∀ i, s.signedInfDist i p = r ∨ s.signedInfDist i p = -r := + fun i ↦ eq_or_eq_neg_of_abs_eq (h i) + refine ⟨{i ∈ (Finset.univ : Finset (Fin (n + 1))) | s.signedInfDist i p = -r}, ?_⟩ + apply (s.exists_forall_signedInfDist_eq_iff_excenterExists_and_eq_excenter hp).1 + refine ⟨r, ?_⟩ + simp only [Set.mem_setOf_eq, ite_mul, neg_mul, one_mul] + intro i + split_ifs with hi + · simpa using hi + · simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hi + simpa [hi] using h' i + · rintro ⟨signs, h⟩ + replace h := (s.exists_forall_signedInfDist_eq_iff_excenterExists_and_eq_excenter hp).2 h + rcases h with ⟨r, h⟩ + refine ⟨|r|, ?_⟩ + simp [h, abs_ite] + +end Simplex + +end Affine diff --git a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean index 559d4d74dc5cc6..4e0afe13ef4395 100644 --- a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean +++ b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean @@ -418,8 +418,6 @@ bundles. -/ TangentBundle (I.prod I') (M × M') ≃ (TangentBundle I M) × (TangentBundle I' M') where toFun p := (⟨p.1.1, p.2.1⟩, ⟨p.1.2, p.2.2⟩) invFun p := ⟨(p.1.1, p.2.1), (p.1.2, p.2.2)⟩ - left_inv _ := rfl - right_inv _ := rfl lemma equivTangentBundleProd_eq_tangentMap_prod_tangentMap : equivTangentBundleProd I M I' M' = fun (p : TangentBundle (I.prod I') (M × M')) ↦ diff --git a/Mathlib/Geometry/Manifold/Diffeomorph.lean b/Mathlib/Geometry/Manifold/Diffeomorph.lean index 580997ba3ae378..8172ff479c9b49 100644 --- a/Mathlib/Geometry/Manifold/Diffeomorph.lean +++ b/Mathlib/Geometry/Manifold/Diffeomorph.lean @@ -12,17 +12,17 @@ This file implements diffeomorphisms. ## Definitions -* `Diffeomorph I I' M M' n`: `n`-times continuously differentiable diffeomorphism between +* `Diffeomorph I I' M M' n`: `n`-times continuously differentiable diffeomorphism between `M` and `M'` with respect to I and I'; we do not introduce a separate definition for the case - `n = ∞`; we use notation instead. + `n = ∞` or `n = ω`; we use notation instead. * `Diffeomorph.toHomeomorph`: reinterpret a diffeomorphism as a homeomorphism. * `ContinuousLinearEquiv.toDiffeomorph`: reinterpret a continuous equivalence as a diffeomorphism. -* `ModelWithCorners.transDiffeomorph`: compose a given `ModelWithCorners` with a diffeomorphism - between the old and the new target spaces. Useful, e.g, to turn any finite dimensional manifold - into a manifold modelled on a Euclidean space. -* `Diffeomorph.toTransDiffeomorph`: the identity diffeomorphism between `M` with model `I` and `M` - with model `I.trans_diffeomorph e`. +* `ModelWithCorners.transContinuousLinearEquiv`: compose a given `ModelWithCorners` with a + continuous linear equiv between the old and the new target spaces. Useful, e.g, to turn any + finite dimensional manifold into a manifold modelled on a Euclidean space. +* `Diffeomorph.toTransContinuousLinearEquiv`: the identity diffeomorphism between `M` with + model `I` and `M` with model `I.transContinuousLinearEquiv e`. This file also provides diffeomorphisms related to products and disjoint unions. * `Diffeomorph.prodCongr`: the product of two diffeomorphisms @@ -351,14 +351,16 @@ theorem uniqueMDiffOn_preimage (h : M ≃ₘ^n⟮I, J⟯ N) (hn : 1 ≤ n) {s : UniqueMDiffOn I (h ⁻¹' s) ↔ UniqueMDiffOn J s := h.symm_image_eq_preimage s ▸ h.symm.uniqueMDiffOn_image hn --- Porting note (https://github.com/leanprover-community/mathlib4/issues/11215): TODO: should use `E ≃ₘ^n[𝕜] F` notation +-- Porting note (https://github.com/leanprover-community/mathlib4/issues/11215): +-- TODO: should use `E ≃ₘ^n[𝕜] F` notation @[simp] theorem uniqueDiffOn_image (h : E ≃ₘ^n⟮𝓘(𝕜, E), 𝓘(𝕜, F)⟯ F) (hn : 1 ≤ n) {s : Set E} : UniqueDiffOn 𝕜 (h '' s) ↔ UniqueDiffOn 𝕜 s := by simp only [← uniqueMDiffOn_iff_uniqueDiffOn, uniqueMDiffOn_image, hn] @[simp] --- Porting note (https://github.com/leanprover-community/mathlib4/issues/11215): TODO: should use `E ≃ₘ^n[𝕜] F` notation +-- Porting note (https://github.com/leanprover-community/mathlib4/issues/11215): +-- TODO: should use `E ≃ₘ^n[𝕜] F` notation theorem uniqueDiffOn_preimage (h : E ≃ₘ^n⟮𝓘(𝕜, E), 𝓘(𝕜, F)⟯ F) (hn : 1 ≤ n) {s : Set F} : UniqueDiffOn 𝕜 (h ⁻¹' s) ↔ UniqueDiffOn 𝕜 s := h.symm_image_eq_preimage s ▸ h.symm.uniqueDiffOn_image hn @@ -391,15 +393,13 @@ end ContinuousLinearEquiv namespace ModelWithCorners -variable (I) (e : E ≃ₘ^n⟮𝓘(𝕜, E), 𝓘(𝕜, E')⟯ E') [NeZero n] +variable (I) (e : E ≃L[𝕜] E') -/-- Apply a diffeomorphism (e.g., a continuous linear equivalence) to the model vector space. -/ -def transDiffeomorph : ModelWithCorners 𝕜 E' H where +/-- Apply a continuous linear equivalence to the model vector space. -/ +def transContinuousLinearEquiv : ModelWithCorners 𝕜 E' H where toPartialEquiv := I.toPartialEquiv.trans e.toEquiv.toPartialEquiv source_eq := by simp - uniqueDiffOn' := by - have hn : 1 ≤ n := ENat.one_le_iff_ne_zero_withTop.mpr (NeZero.ne n) - simp [I.uniqueDiffOn, hn] + uniqueDiffOn' := by simp [I.uniqueDiffOn] target_subset_closure_interior := by simp only [PartialEquiv.trans_target, Equiv.toPartialEquiv_target, Equiv.toPartialEquiv_symm_apply, Diffeomorph.toEquiv_coe_symm, target_eq, univ_inter] @@ -412,116 +412,176 @@ def transDiffeomorph : ModelWithCorners 𝕜 E' H where continuous_toFun := e.continuous.comp I.continuous continuous_invFun := I.continuous_symm.comp e.symm.continuous +@[deprecated (since := "2025-06-12")] alias transDiffeomorph := transContinuousLinearEquiv + @[simp, mfld_simps] -theorem coe_transDiffeomorph : ⇑(I.transDiffeomorph e) = e ∘ I := +theorem coe_transContinuousLinearEquiv : ⇑(I.transContinuousLinearEquiv e) = e ∘ I := rfl +@[deprecated (since := "2025-06-12")] alias coe_transDiffeomorph := coe_transContinuousLinearEquiv + @[simp, mfld_simps] -theorem coe_transDiffeomorph_symm : ⇑(I.transDiffeomorph e).symm = I.symm ∘ e.symm := - rfl +theorem coe_transContinuousLinearEquiv_symm : + ⇑(I.transContinuousLinearEquiv e).symm = I.symm ∘ e.symm := rfl + +@[deprecated (since := "2025-06-12")] +alias coe_transDiffeomorph_symm := coe_transContinuousLinearEquiv_symm -theorem transDiffeomorph_range : range (I.transDiffeomorph e) = e '' range I := +theorem transContinuousLinearEquiv_range : range (I.transContinuousLinearEquiv e) = e '' range I := range_comp e I -theorem coe_extChartAt_transDiffeomorph (x : M) : - ⇑(extChartAt (I.transDiffeomorph e) x) = e ∘ extChartAt I x := +@[deprecated (since := "2025-06-12")] +alias transDiffeomorph_range := transContinuousLinearEquiv_range + +theorem coe_extChartAt_transContinuousLinearEquiv (x : M) : + ⇑(extChartAt (I.transContinuousLinearEquiv e) x) = e ∘ extChartAt I x := rfl -theorem coe_extChartAt_transDiffeomorph_symm (x : M) : - ⇑(extChartAt (I.transDiffeomorph e) x).symm = (extChartAt I x).symm ∘ e.symm := +@[deprecated (since := "2025-06-12")] +alias coe_extChartAt_transDiffeomorph := coe_extChartAt_transContinuousLinearEquiv + +theorem coe_extChartAt_transContinuousLinearEquiv_symm (x : M) : + ⇑(extChartAt (I.transContinuousLinearEquiv e) x).symm = (extChartAt I x).symm ∘ e.symm := rfl -theorem extChartAt_transDiffeomorph_target (x : M) : - (extChartAt (I.transDiffeomorph e) x).target = e.symm ⁻¹' (extChartAt I x).target := by - simp only [e.range_comp, preimage_preimage, mfld_simps]; rfl +@[deprecated (since := "2025-06-12")] +alias coe_extChartAt_transDiffeomorph_symm := coe_extChartAt_transContinuousLinearEquiv_symm -end ModelWithCorners +theorem extChartAt_transContinuousLinearEquiv_target (x : M) : + (extChartAt (I.transContinuousLinearEquiv e) x).target + = e.symm ⁻¹' (extChartAt I x).target := by + simp only [range_comp, preimage_preimage, ContinuousLinearEquiv.image_eq_preimage, mfld_simps] + rfl -namespace Diffeomorph +@[deprecated (since := "2025-06-12")] +alias extChartAt_transDiffeomorph_target := extChartAt_transContinuousLinearEquiv_target -section +end ModelWithCorners + +namespace ContinuousLinearEquiv -variable [NeZero n] (e : E ≃ₘ^n⟮𝓘(𝕜, E), 𝓘(𝕜, F)⟯ F) +variable (e : E ≃L[𝕜] F) -instance instIsManifoldTransDiffeomorph [IsManifold I n M] : - IsManifold (I.transDiffeomorph e) n M := by - refine isManifold_of_contDiffOn (I.transDiffeomorph e) n M fun e₁ e₂ h₁ h₂ => ?_ +instance instIsManifoldtransContinuousLinearEquiv [IsManifold I n M] : + IsManifold (I.transContinuousLinearEquiv e) n M := by + refine isManifold_of_contDiffOn (I.transContinuousLinearEquiv e) n M fun e₁ e₂ h₁ h₂ => ?_ refine e.contDiff.comp_contDiffOn (((contDiffGroupoid n I).compatible h₁ h₂).1.comp e.symm.contDiff.contDiffOn ?_) - simp only [mapsTo_iff_subset_preimage] - mfld_set_tac + simp [preimage_comp, range_comp, mapsTo_iff_subset_preimage, + ContinuousLinearEquiv.image_eq_preimage] variable (I M) /-- The identity diffeomorphism between a manifold with model `I` and the same manifold with model `I.trans_diffeomorph e`. -/ -def toTransDiffeomorph (e : E ≃ₘ^n⟮𝓘(𝕜, E), 𝓘(𝕜, F)⟯ F) : M ≃ₘ^n⟮I, I.transDiffeomorph e⟯ M where +def toTransContinuousLinearEquiv (e : E ≃L[𝕜] F) : M ≃ₘ^n⟮I, I.transContinuousLinearEquiv e⟯ M where toEquiv := Equiv.refl M contMDiff_toFun x := by refine contMDiffWithinAt_iff'.2 ⟨continuousWithinAt_id, ?_⟩ refine e.contDiff.contDiffWithinAt.congr_of_mem (fun y hy ↦ ?_) ?_ - · simp only [Equiv.coe_refl, id, (· ∘ ·), I.coe_extChartAt_transDiffeomorph, + · simp only [Equiv.coe_refl, id, (· ∘ ·), I.coe_extChartAt_transContinuousLinearEquiv, (extChartAt I x).right_inv hy.1] · exact ⟨(extChartAt I x).map_source (mem_extChartAt_source x), trivial, by simp only [mfld_simps]⟩ contMDiff_invFun x := by refine contMDiffWithinAt_iff'.2 ⟨continuousWithinAt_id, ?_⟩ refine e.symm.contDiff.contDiffWithinAt.congr_of_mem (fun y hy => ?_) ?_ - · simp only [mem_inter_iff, I.extChartAt_transDiffeomorph_target] at hy + · simp only [mem_inter_iff, I.extChartAt_transContinuousLinearEquiv_target] at hy simp only [Equiv.coe_refl, Equiv.refl_symm, id, (· ∘ ·), - I.coe_extChartAt_transDiffeomorph_symm, (extChartAt I x).right_inv hy.1] + I.coe_extChartAt_transContinuousLinearEquiv_symm, (extChartAt I x).right_inv hy.1] exact ⟨(extChartAt _ x).map_source (mem_extChartAt_source x), trivial, by simp only [e.symm_apply_apply, Equiv.refl_symm, Equiv.coe_refl, mfld_simps]⟩ +@[deprecated (since := "2025-06-12")] +alias _root_.Diffeomorph.toTransDiffeomorph := toTransContinuousLinearEquiv + variable {I M} @[simp] -theorem contMDiffWithinAt_transDiffeomorph_right {f : M' → M} {x s} : - ContMDiffWithinAt I' (I.transDiffeomorph e) n f s x ↔ ContMDiffWithinAt I' I n f s x := - (toTransDiffeomorph I M e).contMDiffWithinAt_diffeomorph_comp_iff le_rfl +theorem contMDiffWithinAt_transContinuousLinearEquiv_right {f : M' → M} {x s} : + ContMDiffWithinAt I' (I.transContinuousLinearEquiv e) n f s x + ↔ ContMDiffWithinAt I' I n f s x := + (toTransContinuousLinearEquiv I M e).contMDiffWithinAt_diffeomorph_comp_iff le_rfl + +@[deprecated (since := "2025-06-12")] +alias _root_.Diffeomorph.contMDiffWithinAt_transDiffeomorph_right := +contMDiffWithinAt_transContinuousLinearEquiv_right @[simp] -theorem contMDiffAt_transDiffeomorph_right {f : M' → M} {x} : - ContMDiffAt I' (I.transDiffeomorph e) n f x ↔ ContMDiffAt I' I n f x := - (toTransDiffeomorph I M e).contMDiffAt_diffeomorph_comp_iff le_rfl +theorem contMDiffAt_transContinuousLinearEquiv_right {f : M' → M} {x} : + ContMDiffAt I' (I.transContinuousLinearEquiv e) n f x ↔ ContMDiffAt I' I n f x := + (toTransContinuousLinearEquiv I M e).contMDiffAt_diffeomorph_comp_iff le_rfl + +@[deprecated (since := "2025-06-12")] +alias _root_.Diffeomorph.contMDiffAt_transDiffeomorph_right := +contMDiffAt_transContinuousLinearEquiv_right @[simp] -theorem contMDiffOn_transDiffeomorph_right {f : M' → M} {s} : - ContMDiffOn I' (I.transDiffeomorph e) n f s ↔ ContMDiffOn I' I n f s := - (toTransDiffeomorph I M e).contMDiffOn_diffeomorph_comp_iff le_rfl +theorem contMDiffOn_transContinuousLinearEquiv_right {f : M' → M} {s} : + ContMDiffOn I' (I.transContinuousLinearEquiv e) n f s ↔ ContMDiffOn I' I n f s := + (toTransContinuousLinearEquiv I M e).contMDiffOn_diffeomorph_comp_iff le_rfl + +@[deprecated (since := "2025-06-12")] +alias _root_.Diffeomorph.contMDiffOn_transDiffeomorph_right := +contMDiffOn_transContinuousLinearEquiv_right @[simp] -theorem contMDiff_transDiffeomorph_right {f : M' → M} : - ContMDiff I' (I.transDiffeomorph e) n f ↔ ContMDiff I' I n f := - (toTransDiffeomorph I M e).contMDiff_diffeomorph_comp_iff le_rfl +theorem contMDiff_transContinuousLinearEquiv_right {f : M' → M} : + ContMDiff I' (I.transContinuousLinearEquiv e) n f ↔ ContMDiff I' I n f := + (toTransContinuousLinearEquiv I M e).contMDiff_diffeomorph_comp_iff le_rfl + +@[deprecated (since := "2025-06-12")] +alias _root_.Diffeomorph.contMDiff_transDiffeomorph_right := +contMDiff_transContinuousLinearEquiv_right @[deprecated (since := "2024-11-21")] -alias smooth_transDiffeomorph_right := contMDiff_transDiffeomorph_right +alias _root_.Diffeomorph.smooth_transDiffeomorph_right := +contMDiff_transContinuousLinearEquiv_right @[simp] -theorem contMDiffWithinAt_transDiffeomorph_left {f : M → M'} {x s} : - ContMDiffWithinAt (I.transDiffeomorph e) I' n f s x ↔ ContMDiffWithinAt I I' n f s x := - ((toTransDiffeomorph I M e).contMDiffWithinAt_comp_diffeomorph_iff le_rfl).symm +theorem contMDiffWithinAt_transContinuousLinearEquiv_left {f : M → M'} {x s} : + ContMDiffWithinAt (I.transContinuousLinearEquiv e) I' n f s x + ↔ ContMDiffWithinAt I I' n f s x := + ((toTransContinuousLinearEquiv I M e).contMDiffWithinAt_comp_diffeomorph_iff le_rfl).symm + +@[deprecated (since := "2025-06-12")] +alias _root_.Diffeomorph.contMDiffWithinAt_transDiffeomorph_left := +contMDiffWithinAt_transContinuousLinearEquiv_left @[simp] -theorem contMDiffAt_transDiffeomorph_left {f : M → M'} {x} : - ContMDiffAt (I.transDiffeomorph e) I' n f x ↔ ContMDiffAt I I' n f x := - ((toTransDiffeomorph I M e).contMDiffAt_comp_diffeomorph_iff le_rfl).symm +theorem contMDiffAt_transContinuousLinearEquiv_left {f : M → M'} {x} : + ContMDiffAt (I.transContinuousLinearEquiv e) I' n f x ↔ ContMDiffAt I I' n f x := + ((toTransContinuousLinearEquiv I M e).contMDiffAt_comp_diffeomorph_iff le_rfl).symm + +@[deprecated (since := "2025-06-12")] +alias _root_.Diffeomorph.contMDiffAt_transDiffeomorph_left := +contMDiffAt_transContinuousLinearEquiv_left @[simp] -theorem contMDiffOn_transDiffeomorph_left {f : M → M'} {s} : - ContMDiffOn (I.transDiffeomorph e) I' n f s ↔ ContMDiffOn I I' n f s := - ((toTransDiffeomorph I M e).contMDiffOn_comp_diffeomorph_iff le_rfl).symm +theorem contMDiffOn_transContinuousLinearEquiv_left {f : M → M'} {s} : + ContMDiffOn (I.transContinuousLinearEquiv e) I' n f s ↔ ContMDiffOn I I' n f s := + ((toTransContinuousLinearEquiv I M e).contMDiffOn_comp_diffeomorph_iff le_rfl).symm + +@[deprecated (since := "2025-06-12")] +alias _root_.Diffeomorph.contMDiffOn_transDiffeomorph_left := +contMDiffOn_transContinuousLinearEquiv_left @[simp] -theorem contMDiff_transDiffeomorph_left {f : M → M'} : - ContMDiff (I.transDiffeomorph e) I' n f ↔ ContMDiff I I' n f := - ((toTransDiffeomorph I M e).contMDiff_comp_diffeomorph_iff le_rfl).symm +theorem contMDiff_transContinuousLinearEquiv_left {f : M → M'} : + ContMDiff (I.transContinuousLinearEquiv e) I' n f ↔ ContMDiff I I' n f := + ((toTransContinuousLinearEquiv I M e).contMDiff_comp_diffeomorph_iff le_rfl).symm + +@[deprecated (since := "2025-06-12")] +alias _root_.Diffeomorph.contMDiff_transDiffeomorph_left := +contMDiff_transContinuousLinearEquiv_left @[deprecated (since := "2024-11-21")] -alias smooth_transDiffeomorph_left := contMDiff_transDiffeomorph_left +alias _root_.Diffeomorph.smooth_transContinuousLinearEquiv_left := +contMDiff_transContinuousLinearEquiv_left -end +end ContinuousLinearEquiv + +namespace Diffeomorph section Constructions diff --git a/Mathlib/Geometry/RingedSpace/OpenImmersion.lean b/Mathlib/Geometry/RingedSpace/OpenImmersion.lean index 02f4116fee9ec2..f29e42110e8782 100644 --- a/Mathlib/Geometry/RingedSpace/OpenImmersion.lean +++ b/Mathlib/Geometry/RingedSpace/OpenImmersion.lean @@ -266,9 +266,7 @@ theorem to_iso [h' : Epi f.base] : IsIso f := by let t : X ≃ₜ Y := H.base_open.isEmbedding.toHomeomorph.trans { toFun := Subtype.val invFun := fun x => - ⟨x, by rw [Set.range_eq_univ.mpr ((TopCat.epi_iff_surjective _).mp h')]; trivial⟩ - left_inv := fun ⟨_, _⟩ => rfl - right_inv := fun _ => rfl } + ⟨x, by rw [Set.range_eq_univ.mpr ((TopCat.epi_iff_surjective _).mp h')]; trivial⟩ } exact (TopCat.isoOfHomeo t).isIso_hom instance stalk_iso [HasColimits C] (x : X) : IsIso (f.stalkMap x) := by diff --git a/Mathlib/GroupTheory/Abelianization.lean b/Mathlib/GroupTheory/Abelianization.lean index 7d6492ba450b14..e0dc556a0e63c8 100644 --- a/Mathlib/GroupTheory/Abelianization.lean +++ b/Mathlib/GroupTheory/Abelianization.lean @@ -158,7 +158,6 @@ theorem commutator_subset_ker : commutator G ≤ f.ker := by def lift : (G →* A) ≃ (Abelianization G →* A) where toFun f := QuotientGroup.lift _ f fun _ h => MonoidHom.mem_ker.2 <| commutator_subset_ker _ h invFun F := F.comp of - left_inv _ := MonoidHom.ext fun _ => rfl right_inv _ := MonoidHom.ext fun x => QuotientGroup.induction_on x fun _ => rfl @[simp] @@ -261,7 +260,6 @@ def Abelianization.equivOfComm {H : Type*} [CommGroup H] : H ≃* Abelianization { Abelianization.of with toFun := Abelianization.of invFun := Abelianization.lift (MonoidHom.id H) - left_inv := fun _ => rfl right_inv := by rintro ⟨a⟩ rfl } diff --git a/Mathlib/GroupTheory/Congruence/Opposite.lean b/Mathlib/GroupTheory/Congruence/Opposite.lean index 757c4b47b9e4a5..ec8eba83a6e1ec 100644 --- a/Mathlib/GroupTheory/Congruence/Opposite.lean +++ b/Mathlib/GroupTheory/Congruence/Opposite.lean @@ -51,8 +51,6 @@ on `Mᵃᵒᵖ`"] def orderIsoOp : Con M ≃o Con Mᵐᵒᵖ where toFun := op invFun := unop - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' {c d} := by rw [le_def, le_def]; constructor <;> intro h _ _ h' <;> exact h h' end Con diff --git a/Mathlib/GroupTheory/Coprod/Basic.lean b/Mathlib/GroupTheory/Coprod/Basic.lean index e8fe4078847e71..9372e18be9f6fa 100644 --- a/Mathlib/GroupTheory/Coprod/Basic.lean +++ b/Mathlib/GroupTheory/Coprod/Basic.lean @@ -451,7 +451,6 @@ theorem comp_lift {P' : Type*} [Monoid P'] (f : P →* P') (g₁ : M →* P) (g def liftEquiv : (M →* P) × (N →* P) ≃ (M ∗ N →* P) where toFun fg := lift fg.1 fg.2 invFun f := (f.comp inl, f.comp inr) - left_inv _ := rfl right_inv _ := Eq.symm <| lift_unique rfl rfl @[to_additive (attr := simp)] diff --git a/Mathlib/GroupTheory/CoprodI.lean b/Mathlib/GroupTheory/CoprodI.lean index 57973f581d1ed9..81657588017432 100644 --- a/Mathlib/GroupTheory/CoprodI.lean +++ b/Mathlib/GroupTheory/CoprodI.lean @@ -695,9 +695,10 @@ theorem of_word (w : Word M) (h : w ≠ empty) : ∃ (i j : _) (w' : NeWord M i ext rw [h] obtain ⟨l, hnot1, hchain⟩ := w - induction' l with x l hi - · contradiction - · rw [List.forall_mem_cons] at hnot1 + induction l with + | nil => contradiction + | cons x l hi => + rw [List.forall_mem_cons] at hnot1 rcases l with - | ⟨y, l⟩ · refine ⟨x.1, x.1, singleton x.2 hnot1.1, ?_⟩ simp [toWord] diff --git a/Mathlib/GroupTheory/Coxeter/Basic.lean b/Mathlib/GroupTheory/Coxeter/Basic.lean index 81fab6240e43e1..c00a49cfd75d33 100644 --- a/Mathlib/GroupTheory/Coxeter/Basic.lean +++ b/Mathlib/GroupTheory/Coxeter/Basic.lean @@ -370,9 +370,9 @@ theorem wordProd_concat (i : B) (ω : List B) : π (ω.concat i) = π ω * s i : theorem wordProd_append (ω ω' : List B) : π (ω ++ ω') = π ω * π ω' := by simp [wordProd] @[simp] theorem wordProd_reverse (ω : List B) : π (reverse ω) = (π ω)⁻¹ := by - induction' ω with x ω' ih - · simp - · simpa [wordProd_cons, wordProd_append] using ih + induction ω with + | nil => simp + | cons x ω' ih => simpa [wordProd_cons, wordProd_append] using ih theorem wordProd_surjective : Surjective cs.wordProd := by intro w @@ -397,9 +397,10 @@ theorem alternatingWord_succ (i i' : B) (m : ℕ) : theorem alternatingWord_succ' (i i' : B) (m : ℕ) : alternatingWord i i' (m + 1) = (if Even m then i' else i) :: alternatingWord i i' m := by - induction' m with m ih generalizing i i' - · simp [alternatingWord] - · rw [alternatingWord] + induction m generalizing i i' with + | zero => simp [alternatingWord] + | succ m ih => + rw [alternatingWord] nth_rw 1 [ih i' i] rw [alternatingWord] simp [Nat.even_add_one, ← Nat.not_even_iff_odd] @@ -407,9 +408,9 @@ theorem alternatingWord_succ' (i i' : B) (m : ℕ) : @[simp] theorem length_alternatingWord (i i' : B) (m : ℕ) : List.length (alternatingWord i i' m) = m := by - induction' m with m ih generalizing i i' - · dsimp [alternatingWord] - · simpa [alternatingWord] using ih i' i + induction m generalizing i i' with + | zero => dsimp [alternatingWord] + | succ m ih => simpa [alternatingWord] using ih i' i lemma getElem_alternatingWord (i j : B) (p k : ℕ) (hk : k < p) : (alternatingWord i j p)[k]'(by simp [hk]) = (if Even (p + k) then i else j) := by @@ -487,9 +488,10 @@ lemma listTake_succ_alternatingWord (i j : B) (p : ℕ) (k : ℕ) (h : k + 1 < 2 theorem prod_alternatingWord_eq_mul_pow (i i' : B) (m : ℕ) : π (alternatingWord i i' m) = (if Even m then 1 else s i') * (s i * s i') ^ (m / 2) := by - induction' m with m ih - · simp [alternatingWord] - · rw [alternatingWord_succ', wordProd_cons, ih] + induction m with + | zero => simp [alternatingWord] + | succ m ih => + rw [alternatingWord_succ', wordProd_cons, ih] by_cases hm : Even m · have h₁ : ¬ Even (m + 1) := by simp [hm, parity_simps] have h₂ : (m + 1) / 2 = m / 2 := Nat.succ_div_of_not_dvd <| by rwa [← even_iff_two_dvd] diff --git a/Mathlib/GroupTheory/Coxeter/Inversion.lean b/Mathlib/GroupTheory/Coxeter/Inversion.lean index 1d03a2f84f7bdd..ea7cbad11af3a2 100644 --- a/Mathlib/GroupTheory/Coxeter/Inversion.lean +++ b/Mathlib/GroupTheory/Coxeter/Inversion.lean @@ -210,9 +210,10 @@ local prefix:100 "lis" => cs.leftInvSeq theorem rightInvSeq_concat (ω : List B) (i : B) : ris (ω.concat i) = (List.map (MulAut.conj (s i)) (ris ω)).concat (s i) := by - induction' ω with j ω ih - · simp - · dsimp [rightInvSeq, concat] + induction ω with + | nil => simp + | cons j ω ih => + dsimp [rightInvSeq, concat] rw [ih] simp only [concat_eq_append, wordProd_append, wordProd_cons, wordProd_nil, mul_one, mul_inv_rev, inv_simple, cons_append, cons.injEq, and_true] @@ -220,9 +221,10 @@ theorem rightInvSeq_concat (ω : List B) (i : B) : private theorem leftInvSeq_eq_reverse_rightInvSeq_reverse (ω : List B) : lis ω = (ris ω.reverse).reverse := by - induction' ω with i ω ih - · simp - · rw [leftInvSeq, reverse_cons, ← concat_eq_append, rightInvSeq_concat, ih] + induction ω with + | nil => simp + | cons i ω ih => + rw [leftInvSeq, reverse_cons, ← concat_eq_append, rightInvSeq_concat, ih] simp [map_reverse] theorem leftInvSeq_concat (ω : List B) (i : B) : @@ -238,9 +240,9 @@ theorem leftInvSeq_reverse (ω : List B) : simp [leftInvSeq_eq_reverse_rightInvSeq_reverse] @[simp] theorem length_rightInvSeq (ω : List B) : (ris ω).length = ω.length := by - induction' ω with i ω ih - · simp - · simpa [rightInvSeq] + induction ω with + | nil => simp + | cons i ω ih => simpa [rightInvSeq] @[simp] theorem length_leftInvSeq (ω : List B) : (lis ω).length = ω.length := by simp [leftInvSeq_eq_reverse_rightInvSeq_reverse] @@ -250,9 +252,10 @@ theorem getD_rightInvSeq (ω : List B) (j : ℕ) : (π (ω.drop (j + 1)))⁻¹ * (Option.map (cs.simple) ω[j]?).getD 1 * π (ω.drop (j + 1)) := by - induction' ω with i ω ih generalizing j - · simp - · dsimp only [rightInvSeq] + induction ω generalizing j with + | nil => simp + | cons i ω ih => + dsimp only [rightInvSeq] rcases j with _ | j' · simp [getD_cons_zero] · simp only [getD_eq_getElem?_getD] at ih @@ -270,9 +273,10 @@ theorem getD_leftInvSeq (ω : List B) (j : ℕ) : π (ω.take j) * (Option.map (cs.simple) ω[j]?).getD 1 * (π (ω.take j))⁻¹ := by - induction' ω with i ω ih generalizing j - · simp - · dsimp [leftInvSeq] + induction ω generalizing j with + | nil => simp + | cons i ω ih => + dsimp [leftInvSeq] rcases j with _ | j' · simp [getD_cons_zero] · rw [getD_cons_succ] @@ -307,11 +311,12 @@ theorem getD_leftInvSeq_mul_self (ω : List B) (j : ℕ) : theorem rightInvSeq_drop (ω : List B) (j : ℕ) : ris (ω.drop j) = (ris ω).drop j := by - induction' j with j ih₁ generalizing ω - · simp - · induction' ω with k ω _ - · simp - · rw [drop_succ_cons, ih₁ ω, rightInvSeq, drop_succ_cons] + induction j generalizing ω with + | zero => simp + | succ j ih₁ => + induction ω with + | nil => simp + | cons k ω _ => rw [drop_succ_cons, ih₁ ω, rightInvSeq, drop_succ_cons] theorem leftInvSeq_take (ω : List B) (j : ℕ) : lis (ω.take j) = (lis ω).take j := by @@ -323,9 +328,10 @@ theorem leftInvSeq_take (ω : List B) (j : ℕ) : theorem isReflection_of_mem_rightInvSeq (ω : List B) {t : W} (ht : t ∈ ris ω) : cs.IsReflection t := by - induction' ω with i ω ih - · simp at ht - · dsimp [rightInvSeq] at ht + induction ω with + | nil => simp at ht + | cons i ω ih => + dsimp [rightInvSeq] at ht rcases ht with _ | ⟨_, mem⟩ · use (π ω)⁻¹, i group @@ -385,9 +391,9 @@ theorem isLeftInversion_of_mem_leftInvSeq {ω : List B} (hω : cs.IsReduced ω) _ = ℓ (π ω) := hω.symm theorem prod_rightInvSeq (ω : List B) : prod (ris ω) = (π ω)⁻¹ := by - induction' ω with i ω ih - · simp - · simp [rightInvSeq, ih, wordProd_cons] + induction ω with + | nil => simp + | cons i ω ih => simp [rightInvSeq, ih, wordProd_cons] theorem prod_leftInvSeq (ω : List B) : prod (lis ω) = (π ω)⁻¹ := by simp only [leftInvSeq_eq_reverse_rightInvSeq_reverse, prod_reverse_noncomm, inv_inj] diff --git a/Mathlib/GroupTheory/Coxeter/Length.lean b/Mathlib/GroupTheory/Coxeter/Length.lean index 3a8c79a8634113..89d752213b67ac 100644 --- a/Mathlib/GroupTheory/Coxeter/Length.lean +++ b/Mathlib/GroupTheory/Coxeter/Length.lean @@ -244,8 +244,8 @@ theorem IsReduced.drop {cs : CoxeterSystem M W} {ω : List B} (hω : cs.IsReduce theorem not_isReduced_alternatingWord (i i' : B) {m : ℕ} (hM : M i i' ≠ 0) (hm : m > M i i') : ¬cs.IsReduced (alternatingWord i i' m) := by - induction' hm with m _ ih - · -- Base case; m = M i i' + 1 + induction hm with + | refl => -- Base case; m = M i i' + 1 suffices h : ℓ (π (alternatingWord i i' (M i i' + 1))) < M i i' + 1 by unfold IsReduced rw [Nat.succ_eq_add_one, length_alternatingWord] @@ -260,7 +260,7 @@ theorem not_isReduced_alternatingWord (i i' : B) {m : ℕ} (hM : M i i' ≠ 0) ( _ = M i i' - 1 := length_alternatingWord _ _ _ _ ≤ M i i' := Nat.sub_le _ _ _ < M i i' + 1 := Nat.lt_succ_self _ - · -- Inductive step + | step m ih => -- Inductive step contrapose! ih rw [alternatingWord_succ'] at ih apply IsReduced.drop (j := 1) at ih diff --git a/Mathlib/GroupTheory/FreeGroup/Basic.lean b/Mathlib/GroupTheory/FreeGroup/Basic.lean index cbfcfb52e251c4..6ddc2eb9eba284 100644 --- a/Mathlib/GroupTheory/FreeGroup/Basic.lean +++ b/Mathlib/GroupTheory/FreeGroup/Basic.lean @@ -820,7 +820,6 @@ def freeGroupEmptyEquivUnit : FreeGroup Empty ≃ Unit where toFun _ := () invFun _ := 1 left_inv := by rintro ⟨_ | ⟨⟨⟨⟩, _⟩, _⟩⟩; rfl - right_inv := fun ⟨⟩ => rfl /-- The bijection between the free group on a singleton, and the integers. -/ def freeGroupUnitEquivInt : FreeGroup Unit ≃ ℤ where diff --git a/Mathlib/GroupTheory/GroupAction/ConjAct.lean b/Mathlib/GroupTheory/GroupAction/ConjAct.lean index 9b6e5a22f7745a..b7b1c51bbb0477 100644 --- a/Mathlib/GroupTheory/GroupAction/ConjAct.lean +++ b/Mathlib/GroupTheory/GroupAction/ConjAct.lean @@ -67,8 +67,6 @@ instance : Inhabited (ConjAct G) := def ofConjAct : ConjAct G ≃* G where toFun := id invFun := id - left_inv := fun _ => rfl - right_inv := fun _ => rfl map_mul' := fun _ _ => rfl /-- Reinterpret `g : G` as an element of `ConjAct G`. -/ @@ -287,8 +285,6 @@ def unitsCentralizerEquiv (x : Mˣ) : change _ • _ = _ simp only [ConjAct.smul_def, ConjAct.ofConjAct_toConjAct, mul_inv_eq_iff_eq_mul] exact Units.ext <| (u.1.2 x <| Set.mem_singleton _).symm⟩ - left_inv := fun _ ↦ by ext; rfl - right_inv := fun _ ↦ by ext; rfl map_mul' := map_mul _ } end Units diff --git a/Mathlib/GroupTheory/GroupAction/Quotient.lean b/Mathlib/GroupTheory/GroupAction/Quotient.lean index b995dc7840811a..320accd0c62c18 100644 --- a/Mathlib/GroupTheory/GroupAction/Quotient.lean +++ b/Mathlib/GroupTheory/GroupAction/Quotient.lean @@ -386,13 +386,13 @@ noncomputable def equivSubgroupOrbitsQuotientGroup [IsPretransitive α β] rw [← @Quotient.mk''_eq_mk, Quotient.eq'', orbitRel_apply] exact ⟨⟨_, h⟩, by simp [mul_smul]⟩) left_inv := fun y ↦ by - induction' y using Quotient.inductionOn' with y + cases y using Quotient.inductionOn' simp only [Quotient.liftOn'_mk''] rw [← @Quotient.mk''_eq_mk, Quotient.eq'', orbitRel_apply] convert mem_orbit_self _ - rw [inv_smul_eq_iff, (exists_smul_eq α y x).choose_spec] + rw [inv_smul_eq_iff, (exists_smul_eq α _ x).choose_spec] right_inv := fun g ↦ by - induction' g using Quotient.inductionOn' with g + cases g using Quotient.inductionOn' with | _ g simp only [Quotient.liftOn'_mk'', Quotient.liftOn'_mk, QuotientGroup.mk] rw [Quotient.eq'', leftRel_eq] simp only diff --git a/Mathlib/GroupTheory/Index.lean b/Mathlib/GroupTheory/Index.lean index 575456de2c0474..46268e721f6f68 100644 --- a/Mathlib/GroupTheory/Index.lean +++ b/Mathlib/GroupTheory/Index.lean @@ -258,6 +258,10 @@ theorem index_map_eq (hf1 : Surjective f) (hf2 : f.ker ≤ H) : (H.map f).index lemma index_map_of_bijective (hf : Bijective f) (H : Subgroup G) : (H.map f).index = H.index := index_map_eq _ hf.2 (by rw [f.ker_eq_bot_iff.2 hf.1]; exact bot_le) +@[to_additive (attr := simp)] +theorem index_map_equiv (e : G ≃* G') : (map (e : G →* G') H).index = H.index := + index_map_of_bijective e.bijective H + @[to_additive] theorem index_map_of_injective {f : G →* G'} (hf : Function.Injective f) : (H.map f).index = H.index * f.range.index := by @@ -420,6 +424,10 @@ noncomputable def fintypeOfIndexNeZero (hH : H.index ≠ 0) : Fintype (G ⧸ H) lemma index_eq_zero_iff_infinite : H.index = 0 ↔ Infinite (G ⧸ H) := by simp [index_eq_card, Nat.card_eq_zero] +@[to_additive] +lemma index_ne_zero_iff_finite : H.index ≠ 0 ↔ Finite (G ⧸ H) := by + simp [index_eq_zero_iff_infinite] + @[to_additive one_lt_index_of_ne_top] theorem one_lt_index_of_ne_top [Finite (G ⧸ H)] (hH : H ≠ ⊤) : 1 < H.index := Nat.one_lt_iff_ne_zero_and_ne_one.mpr ⟨index_ne_zero_of_finite, mt index_eq_one.mp hH⟩ @@ -563,6 +571,10 @@ instance IsFiniteRelIndex.to_finiteIndex_subgroupOf [H.IsFiniteRelIndex K] : (H.subgroupOf K).FiniteIndex where index_ne_zero := relindex_ne_zero +@[to_additive] +theorem finiteIndex_iff : H.FiniteIndex ↔ H.index ≠ 0 := + ⟨fun h ↦ h.index_ne_zero, fun h ↦ ⟨h⟩⟩ + /-- A finite index subgroup has finite quotient. -/ @[to_additive "A finite index subgroup has finite quotient"] noncomputable def fintypeQuotientOfFiniteIndex [FiniteIndex H] : Fintype (G ⧸ H) := diff --git a/Mathlib/GroupTheory/MonoidLocalization/GrothendieckGroup.lean b/Mathlib/GroupTheory/MonoidLocalization/GrothendieckGroup.lean index d0c8e04021d750..1402c9687cbb89 100644 --- a/Mathlib/GroupTheory/MonoidLocalization/GrothendieckGroup.lean +++ b/Mathlib/GroupTheory/MonoidLocalization/GrothendieckGroup.lean @@ -59,7 +59,7 @@ lemma inv_mk (m : M) (s : (⊤ : Submonoid M)) : (mk m s)⁻¹ = .mk s ⟨m, Sub instance instCommGroup : CommGroup (GrothendieckGroup M) where __ : CommMonoid (GrothendieckGroup M) := inferInstance inv_mul_cancel a := by - induction' a using ind + cases a using ind rw [inv_mk, mk_eq_monoidOf_mk', ←Submonoid.LocalizationMap.mk'_mul] convert Submonoid.LocalizationMap.mk'_self' _ _ rw [mul_comm, Submonoid.coe_mul] diff --git a/Mathlib/GroupTheory/MonoidLocalization/Order.lean b/Mathlib/GroupTheory/MonoidLocalization/Order.lean index d8ee42ac6b8e4e..60a2a088960bc2 100644 --- a/Mathlib/GroupTheory/MonoidLocalization/Order.lean +++ b/Mathlib/GroupTheory/MonoidLocalization/Order.lean @@ -71,9 +71,9 @@ instance partialOrder : PartialOrder (Localization s) where refine (mul_le_mul_left' hab _).trans ?_ rwa [mul_left_comm, mul_left_comm (b.2 : α), mul_le_mul_iff_left] le_antisymm a b := by - induction' a using Localization.rec with a₁ a₂ + induction a using Localization.rec on_goal 1 => - induction' b using Localization.rec with b₁ b₂ + induction b using Localization.rec · simp_rw [mk_le_mk, mk_eq_mk_iff, r_iff_exists] exact fun hab hba => ⟨1, by rw [hab.antisymm hba]⟩ all_goals rfl diff --git a/Mathlib/GroupTheory/Nilpotent.lean b/Mathlib/GroupTheory/Nilpotent.lean index 8e14d4b0d7bafd..a92a248f3f76d5 100644 --- a/Mathlib/GroupTheory/Nilpotent.lean +++ b/Mathlib/GroupTheory/Nilpotent.lean @@ -310,9 +310,9 @@ theorem lowerCentralSeries_succ (n : ℕ) : rfl instance lowerCentralSeries_normal (n : ℕ) : Normal (lowerCentralSeries G n) := by - induction' n with d hd - · exact (⊤ : Subgroup G).normal_of_characteristic - · exact @Subgroup.commutator_normal _ _ (lowerCentralSeries G d) ⊤ hd _ + induction n with + | zero => exact (⊤ : Subgroup G).normal_of_characteristic + | succ d hd => exact @Subgroup.commutator_normal _ _ (lowerCentralSeries G d) ⊤ hd _ theorem lowerCentralSeries_antitone : Antitone (lowerCentralSeries G) := by refine antitone_nat_of_succ_le fun n x hx => ?_ @@ -443,9 +443,10 @@ end Classical theorem lowerCentralSeries_map_subtype_le (H : Subgroup G) (n : ℕ) : (lowerCentralSeries H n).map H.subtype ≤ lowerCentralSeries G n := by - induction' n with d hd - · simp - · rw [lowerCentralSeries_succ, lowerCentralSeries_succ, MonoidHom.map_closure] + induction n with + | zero => simp + | succ d hd => + rw [lowerCentralSeries_succ, lowerCentralSeries_succ, MonoidHom.map_closure] apply Subgroup.closure_mono rintro x1 ⟨x2, ⟨x3, hx3, x4, _hx4, rfl⟩, rfl⟩ exact ⟨x3, hd (mem_map.mpr ⟨x3, hx3, rfl⟩), x4, by simp⟩ @@ -474,17 +475,19 @@ instance (priority := 100) Group.isNilpotent_of_subsingleton [Subsingleton G] : theorem upperCentralSeries.map {H : Type*} [Group H] {f : G →* H} (h : Function.Surjective f) (n : ℕ) : Subgroup.map f (upperCentralSeries G n) ≤ upperCentralSeries H n := by - induction' n with d hd - · simp - · rintro _ ⟨x, hx : x ∈ upperCentralSeries G d.succ, rfl⟩ y' + induction n with + | zero => simp + | succ d hd => + rintro _ ⟨x, hx : x ∈ upperCentralSeries G d.succ, rfl⟩ y' rcases h y' with ⟨y, rfl⟩ simpa using hd (mem_map_of_mem f (hx y)) theorem lowerCentralSeries.map {H : Type*} [Group H] (f : G →* H) (n : ℕ) : Subgroup.map f (lowerCentralSeries G n) ≤ lowerCentralSeries H n := by - induction' n with d hd - · simp - · rintro a ⟨x, hx : x ∈ lowerCentralSeries G d.succ, rfl⟩ + induction n with + | zero => simp + | succ d hd => + rintro a ⟨x, hx : x ∈ lowerCentralSeries G d.succ, rfl⟩ refine closure_induction (hx := hx) ?_ (by simp [f.map_one, Subgroup.one_mem _]) (fun y z _ _ hy hz => by simp [MonoidHom.map_mul, Subgroup.mul_mem _ hy hz]) (fun y _ hy => by rw [f.map_inv]; exact Subgroup.inv_mem _ hy) @@ -568,10 +571,12 @@ private theorem comap_center_subst {H₁ H₂ : Subgroup G} [Normal H₁] [Norma theorem comap_upperCentralSeries_quotient_center (n : ℕ) : comap (mk' (center G)) (upperCentralSeries (G ⧸ center G) n) = upperCentralSeries G n.succ := by - induction' n with n ih - · simp only [upperCentralSeries_zero, MonoidHom.comap_bot, ker_mk', + induction n with + | zero => + simp only [upperCentralSeries_zero, MonoidHom.comap_bot, ker_mk', (upperCentralSeries_one G).symm] - · let Hn := upperCentralSeries (G ⧸ center G) n + | succ n ih => + let Hn := upperCentralSeries (G ⧸ center G) n calc comap (mk' (center G)) (upperCentralSeriesStep Hn) = comap (mk' (center G)) (comap (mk' Hn) (center ((G ⧸ center G) ⧸ Hn))) := by @@ -633,18 +638,19 @@ theorem nilpotent_center_quotient_ind {P : ∀ (G) [Group G] [IsNilpotent G], Pr (hbase : ∀ (G) [Group G] [Subsingleton G], P G) (hstep : ∀ (G) [Group G] [IsNilpotent G], P (G ⧸ center G) → P G) : P G := by obtain ⟨n, h⟩ : ∃ n, Group.nilpotencyClass G = n := ⟨_, rfl⟩ - induction' n with n ih generalizing G - · haveI := nilpotencyClass_zero_iff_subsingleton.mp h + induction n generalizing G with + | zero => + haveI := nilpotencyClass_zero_iff_subsingleton.mp h exact hbase _ - · have hn : Group.nilpotencyClass (G ⧸ center G) = n := by + | succ n ih => + have hn : Group.nilpotencyClass (G ⧸ center G) = n := by simp [nilpotencyClass_quotient_center, h] exact hstep _ (ih _ hn) theorem derived_le_lower_central (n : ℕ) : derivedSeries G n ≤ lowerCentralSeries G n := by - induction' n with i ih - · simp - · apply commutator_mono ih - simp + induction n with + | zero => simp + | succ i ih => apply commutator_mono ih; simp /-- Abelian groups are nilpotent -/ instance (priority := 100) CommGroup.isNilpotent {G : Type*} [CommGroup G] : IsNilpotent G := by @@ -670,9 +676,10 @@ variable {G₁ G₂ : Type*} [Group G₁] [Group G₂] theorem lowerCentralSeries_prod (n : ℕ) : lowerCentralSeries (G₁ × G₂) n = (lowerCentralSeries G₁ n).prod (lowerCentralSeries G₂ n) := by - induction' n with n ih - · simp - · calc + induction n with + | zero => simp + | succ n ih => + calc lowerCentralSeries (G₁ × G₂) n.succ = ⁅lowerCentralSeries (G₁ × G₂) n, ⊤⁆ := rfl _ = ⁅(lowerCentralSeries G₁ n).prod (lowerCentralSeries G₂ n), ⊤⁆ := by rw [ih] _ = ⁅(lowerCentralSeries G₁ n).prod (lowerCentralSeries G₂ n), (⊤ : Subgroup G₁).prod ⊤⁆ := by @@ -708,9 +715,10 @@ theorem lowerCentralSeries_pi_le (n : ℕ) : lowerCentralSeries (∀ i, Gs i) n ≤ Subgroup.pi Set.univ fun i => lowerCentralSeries (Gs i) n := by let pi := fun f : ∀ i, Subgroup (Gs i) => Subgroup.pi Set.univ f - induction' n with n ih - · simp [pi_top] - · calc + induction n with + | zero => simp [pi_top] + | succ n ih => + calc lowerCentralSeries (∀ i, Gs i) n.succ = ⁅lowerCentralSeries (∀ i, Gs i) n, ⊤⁆ := rfl _ ≤ ⁅pi fun i => lowerCentralSeries (Gs i) n, ⊤⁆ := commutator_mono ih (le_refl _) _ = ⁅pi fun i => lowerCentralSeries (Gs i) n, pi fun i => ⊤⁆ := by simp [pi, pi_top] @@ -739,9 +747,10 @@ theorem lowerCentralSeries_pi_of_finite [Finite η] (n : ℕ) : lowerCentralSeries (∀ i, Gs i) n = Subgroup.pi Set.univ fun i => lowerCentralSeries (Gs i) n := by let pi := fun f : ∀ i, Subgroup (Gs i) => Subgroup.pi Set.univ f - induction' n with n ih - · simp [pi_top] - · calc + induction n with + | zero => simp [pi_top] + | succ n ih => + calc lowerCentralSeries (∀ i, Gs i) n.succ = ⁅lowerCentralSeries (∀ i, Gs i) n, ⊤⁆ := rfl _ = ⁅pi fun i => lowerCentralSeries (Gs i) n, ⊤⁆ := by rw [ih] _ = ⁅pi fun i => lowerCentralSeries (Gs i) n, pi fun i => ⊤⁆ := by simp [pi, pi_top] diff --git a/Mathlib/GroupTheory/NoncommPiCoprod.lean b/Mathlib/GroupTheory/NoncommPiCoprod.lean index 33d4b60a80c386..7959f7e5c229a5 100644 --- a/Mathlib/GroupTheory/NoncommPiCoprod.lean +++ b/Mathlib/GroupTheory/NoncommPiCoprod.lean @@ -54,9 +54,10 @@ theorem eq_one_of_noncommProd_eq_one_of_iSupIndep {ι : Type*} (s : Finset ι) ( (heq1 : s.noncommProd f comm = 1) : ∀ i ∈ s, f i = 1 := by classical revert heq1 - induction' s using Finset.induction_on with i s hnotMem ih - · simp - · have hcomm := comm.mono (Finset.coe_subset.2 <| Finset.subset_insert _ _) + induction s using Finset.induction_on with + | empty => simp + | insert i s hnotMem ih => + have hcomm := comm.mono (Finset.coe_subset.2 <| Finset.subset_insert _ _) simp only [Finset.forall_mem_insert] at hmem have hmem_bsupr : s.noncommProd f hcomm ∈ ⨆ i ∈ (s : Set ι), K i := by refine Subgroup.noncommProd_mem _ _ ?_ diff --git a/Mathlib/GroupTheory/OreLocalization/Basic.lean b/Mathlib/GroupTheory/OreLocalization/Basic.lean index f9a593be539682..88fb1a4aaacb70 100644 --- a/Mathlib/GroupTheory/OreLocalization/Basic.lean +++ b/Mathlib/GroupTheory/OreLocalization/Basic.lean @@ -182,7 +182,7 @@ def lift₂Expand {C : Sort*} (P : X → S → X → S → C) have := hP r₁ 1 s₁ (by simp) r₂ t₂ s₂ ht₂ simp [this]) fun r₁ t₁ s₁ ht₁ => by - ext x; induction' x with r₂ s₂ + ext x; cases x with | _ r₂ s₂ dsimp only rw [liftExpand_of, liftExpand_of, hP r₁ t₁ s₁ ht₁ r₂ 1 s₂ (by simp)]; simp @@ -250,7 +250,7 @@ private def smul'' (r : R) (s : S) : X[S⁻¹] → X[S⁻¹] := protected def smul : R[S⁻¹] → X[S⁻¹] → X[S⁻¹] := liftExpand smul'' fun r₁ r₂ s hs => by ext x - induction' x with x s₂ + cases x with | _ x s₂ show OreLocalization.smul' r₁ s x s₂ = OreLocalization.smul' (r₂ * r₁) ⟨_, hs⟩ x s₂ rcases oreCondition r₁ s₂ with ⟨r₁', s₁', h₁⟩ rw [smul'_char _ _ _ _ _ _ h₁] @@ -346,7 +346,7 @@ protected theorem div_eq_one {s : S} : (s : R) /ₒ s = 1 := @[to_additive] protected theorem one_smul (x : X[S⁻¹]) : (1 : R[S⁻¹]) • x = x := by - induction' x with r s + cases x with | _ r s simp [OreLocalization.one_def, oreDiv_smul_char 1 r 1 s 1 s (by simp)] @[to_additive] @@ -355,15 +355,15 @@ protected theorem one_mul (x : R[S⁻¹]) : 1 * x = x := @[to_additive] protected theorem mul_one (x : R[S⁻¹]) : x * 1 = x := by - induction' x with r s + cases x with | _ r s simp [OreLocalization.one_def, oreDiv_mul_char r (1 : R) s (1 : S) r 1 (by simp)] @[to_additive] protected theorem mul_smul (x y : R[S⁻¹]) (z : X[S⁻¹]) : (x * y) • z = x • y • z := by -- Porting note: `assoc_rw` was not ported yet - induction' x with r₁ s₁ - induction' y with r₂ s₂ - induction' z with r₃ s₃ + cases x with | _ r₁ s₁ + cases y with | _ r₂ s₂ + cases z with | _ r₃ s₃ rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha' rcases oreDivSMulChar' r₂ r₃ s₂ s₃ with ⟨rb, sb, hb, hb'⟩; rw [hb']; clear hb' rcases oreCondition ra sb with ⟨rc, sc, hc⟩ @@ -479,8 +479,8 @@ def universalMulHom (hf : ∀ s : S, f s = fS s) : R[S⁻¹] →* T where simp only [one_mul, Units.inv_mul] map_one' := by beta_reduce; rw [OreLocalization.one_def, liftExpand_of]; simp map_mul' x y := by - induction' x with r₁ s₁ - induction' y with r₂ s₂ + cases x with | _ r₁ s₁ + cases y with | _ r₂ s₂ rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha' rw [liftExpand_of, liftExpand_of, liftExpand_of, Units.inv_mul_eq_iff_eq_mul, map_mul, map_mul, Units.val_mul, mul_assoc, ← mul_assoc (fS s₁ : T), ← mul_assoc (fS s₁ : T), Units.mul_inv, @@ -502,7 +502,7 @@ theorem universalMulHom_commutes {r : R} : universalMulHom f fS hf (numeratorHom @[to_additive "The universal morphism `universalAddHom` is unique."] theorem universalMulHom_unique (φ : R[S⁻¹] →* T) (huniv : ∀ r : R, φ (numeratorHom r) = f r) : φ = universalMulHom f fS hf := by - ext x; induction' x with r s + ext x; cases x with | _ r s rw [universalMulHom_apply, ← huniv r, numeratorHom_apply, ← one_mul (φ (r /ₒ s)), ← Units.val_one, ← inv_mul_cancel (fS s), Units.val_mul, mul_assoc, ← hf, ← huniv, ← φ.map_mul, numeratorHom_apply, OreLocalization.mul_cancel] @@ -541,12 +541,12 @@ theorem smul_oreDiv (r : R) (x : X) (s : S) : @[to_additive (attr := simp)] theorem oreDiv_one_smul (r : M) (x : X[S⁻¹]) : (r /ₒ (1 : S)) • x = r • x := by - induction' x using OreLocalization.ind with r' s + cases x rw [smul_oreDiv, oreDiv_smul_oreDiv, mul_one, smul_eq_mul, mul_one] @[to_additive] theorem smul_one_smul (r : R) (x : X[S⁻¹]) : (r • 1 : M) • x = r • x := by - induction' x using OreLocalization.ind with r' s + cases x simp only [smul_oreDiv, smul_eq_mul, mul_one] @[to_additive] @@ -574,8 +574,8 @@ instance : IsScalarTower R M[S⁻¹] X[S⁻¹] where @[to_additive] instance [SMulCommClass R M M] : SMulCommClass R M[S⁻¹] X[S⁻¹] where smul_comm r x y := by - induction' x using OreLocalization.ind with r₁ s₁ - induction' y using OreLocalization.ind with r₂ s₂ + cases x with | _ r₁ s₁ + cases y with | _ r₂ s₂ rw [← smul_one_oreDiv_one_smul, ← smul_one_oreDiv_one_smul, smul_smul, smul_smul, mul_div_one, oreDiv_mul_char _ _ _ _ (r • 1) s₁ (by simp), mul_one] simp @@ -611,8 +611,8 @@ theorem oreDiv_mul_oreDiv_comm {r₁ r₂ : R} {s₁ s₂ : S} : @[to_additive] instance : CommMonoid R[S⁻¹] where mul_comm := fun x y => by - induction' x with r₁ s₁ - induction' y with r₂ s₂ + cases x with | _ r₁ s₁ + cases y with | _ r₂ s₂ rw [oreDiv_mul_oreDiv_comm, oreDiv_mul_oreDiv_comm, mul_comm r₁, mul_comm s₁] end CommMonoid diff --git a/Mathlib/GroupTheory/Perm/Cycle/Concrete.lean b/Mathlib/GroupTheory/Perm/Cycle/Concrete.lean index c8ff7449f44f3f..bfeac53d05e793 100644 --- a/Mathlib/GroupTheory/Perm/Cycle/Concrete.lean +++ b/Mathlib/GroupTheory/Perm/Cycle/Concrete.lean @@ -68,9 +68,10 @@ theorem formPerm_disjoint_iff (hl : Nodup l) (hl' : Nodup l') (hn : 2 ≤ l.leng theorem isCycle_formPerm (hl : Nodup l) (hn : 2 ≤ l.length) : IsCycle (formPerm l) := by rcases l with - | ⟨x, l⟩ · norm_num at hn - induction' l with y l generalizing x - · norm_num at hn - · use x + induction l generalizing x with + | nil => norm_num at hn + | cons y l => + use x constructor · rwa [formPerm_apply_mem_ne_self_iff _ hl _ mem_cons_self] · intro w hw @@ -134,7 +135,7 @@ theorem formPerm_coe (l : List α) (hl : l.Nodup) : formPerm (l : Cycle α) hl = rfl theorem formPerm_subsingleton (s : Cycle α) (h : Subsingleton s) : formPerm s h.nodup = 1 := by - induction' s using Quot.inductionOn with s + obtain ⟨s⟩ := s simp only [formPerm_coe, mk_eq_coe] simp only [length_subsingleton_iff, length_coe, mk_eq_coe] at h obtain - | ⟨hd, tl⟩ := s @@ -149,7 +150,7 @@ theorem isCycle_formPerm (s : Cycle α) (h : Nodup s) (hn : Nontrivial s) : theorem support_formPerm [Fintype α] (s : Cycle α) (h : Nodup s) (hn : Nontrivial s) : support (formPerm s h) = s.toFinset := by - induction' s using Quot.inductionOn with s + obtain ⟨s⟩ := s refine support_formPerm_of_nodup s h ?_ rintro _ rfl simpa [Nat.succ_le_succ_iff] using length_nontrivial hn diff --git a/Mathlib/GroupTheory/Perm/Cycle/Factors.lean b/Mathlib/GroupTheory/Perm/Cycle/Factors.lean index 68ce8b3077a079..d032417ce5b798 100644 --- a/Mathlib/GroupTheory/Perm/Cycle/Factors.lean +++ b/Mathlib/GroupTheory/Perm/Cycle/Factors.lean @@ -808,9 +808,10 @@ theorem cycle_induction_on [Finite β] (P : Perm β → Prop) (σ : Perm β) (ba let x := σ.truncCycleFactors.out exact (congr_arg P x.2.1).mp (this x.1 x.2.2.1 x.2.2.2) intro l - induction' l with σ l ih - · exact fun _ _ => base_one - · intro h1 h2 + induction l with + | nil => exact fun _ _ => base_one + | cons σ l ih => + intro h1 h2 rw [List.prod_cons] exact induction_disjoint σ l.prod (disjoint_prod_right _ (List.pairwise_cons.mp h2).1) diff --git a/Mathlib/GroupTheory/Perm/Cycle/Type.lean b/Mathlib/GroupTheory/Perm/Cycle/Type.lean index 19b7e9212a2202..36e8c8377d4a60 100644 --- a/Mathlib/GroupTheory/Perm/Cycle/Type.lean +++ b/Mathlib/GroupTheory/Perm/Cycle/Type.lean @@ -167,9 +167,10 @@ theorem sign_of_cycleType' (σ : Perm α) : theorem sign_of_cycleType (f : Perm α) : sign f = (-1 : ℤˣ) ^ (f.cycleType.sum + Multiset.card f.cycleType) := by rw [sign_of_cycleType'] - induction' f.cycleType using Multiset.induction_on with a s ihs - · rfl - · rw [Multiset.map_cons, Multiset.prod_cons, Multiset.sum_cons, Multiset.card_cons, ihs] + induction f.cycleType using Multiset.induction_on with + | empty => rfl + | cons a s ihs => + rw [Multiset.map_cons, Multiset.prod_cons, Multiset.sum_cons, Multiset.card_cons, ihs] simp only [pow_add, pow_one, mul_neg_one, neg_mul, mul_neg, mul_assoc, mul_one] @[simp] diff --git a/Mathlib/GroupTheory/Perm/DomMulAct.lean b/Mathlib/GroupTheory/Perm/DomMulAct.lean index 5e722bb92687c5..e056d53f0df79b 100644 --- a/Mathlib/GroupTheory/Perm/DomMulAct.lean +++ b/Mathlib/GroupTheory/Perm/DomMulAct.lean @@ -77,7 +77,6 @@ def stabilizerMulEquiv : (stabilizer (Perm α)ᵈᵐᵃ f)ᵐᵒᵖ ≃* (∀ i, ext a rw [smul_apply, symm_apply_apply, Perm.smul_def] apply comp_stabilizerEquiv_invFun⟩ - left_inv _ := rfl right_inv g := by ext i a; apply stabilizerEquiv_invFun_eq map_mul' _ _ := rfl diff --git a/Mathlib/GroupTheory/Perm/Fin.lean b/Mathlib/GroupTheory/Perm/Fin.lean index 342d14afc0274f..88daa419129522 100644 --- a/Mathlib/GroupTheory/Perm/Fin.lean +++ b/Mathlib/GroupTheory/Perm/Fin.lean @@ -110,10 +110,12 @@ theorem isCycle_finRotate {n : ℕ} : IsCycle (finRotate (n + 2)) := by clear hx' obtain ⟨x, hx⟩ := x rw [zpow_natCast, Fin.ext_iff, Fin.val_mk] - induction' x with x ih; · rfl - rw [pow_succ', Perm.mul_apply, coe_finRotate_of_ne_last, ih (lt_trans x.lt_succ_self hx)] - rw [Ne, Fin.ext_iff, ih (lt_trans x.lt_succ_self hx), Fin.val_last] - exact ne_of_lt (Nat.lt_of_succ_lt_succ hx) + induction x with + | zero => rfl + | succ x ih => + rw [pow_succ', Perm.mul_apply, coe_finRotate_of_ne_last, ih (lt_trans x.lt_succ_self hx)] + rw [Ne, Fin.ext_iff, ih (lt_trans x.lt_succ_self hx), Fin.val_last] + exact ne_of_lt (Nat.lt_of_succ_lt_succ hx) theorem isCycle_finRotate_of_le {n : ℕ} (h : 2 ≤ n) : IsCycle (finRotate n) := by obtain ⟨m, rfl⟩ := exists_add_of_le h diff --git a/Mathlib/GroupTheory/Perm/List.lean b/Mathlib/GroupTheory/Perm/List.lean index 0ebf3c6f2cb2ef..0def8cdca39b40 100644 --- a/Mathlib/GroupTheory/Perm/List.lean +++ b/Mathlib/GroupTheory/Perm/List.lean @@ -105,9 +105,10 @@ theorem formPerm_apply_of_notMem (h : x ∉ l) : formPerm l x = x := theorem formPerm_apply_mem_of_mem (h : x ∈ l) : formPerm l x ∈ l := by rcases l with - | ⟨y, l⟩ · simp at h - induction' l with z l IH generalizing x y - · simpa using h - · by_cases hx : x ∈ z :: l + induction l generalizing x y with + | nil => simpa using h + | cons z l IH => + by_cases hx : x ∈ z :: l · rw [formPerm_cons_cons, mul_apply, swap_apply_def] split_ifs · simp [IH _ hx] @@ -127,14 +128,14 @@ theorem formPerm_mem_iff_mem : l.formPerm x ∈ l ↔ x ∈ l := @[simp] theorem formPerm_cons_concat_apply_last (x y : α) (xs : List α) : formPerm (x :: (xs ++ [y])) y = x := by - induction' xs with z xs IH generalizing x y - · simp - · simp [IH] + induction xs generalizing x y with + | nil => simp + | cons z xs IH => simp [IH] @[simp] theorem formPerm_apply_getLast (x : α) (xs : List α) : formPerm (x :: xs) ((x :: xs).getLast (cons_ne_nil x xs)) = x := by - induction' xs using List.reverseRecOn with xs y _ generalizing x <;> simp + induction xs using List.reverseRecOn generalizing x <;> simp @[simp] theorem formPerm_apply_getElem_length (x : α) (xs : List α) : @@ -159,9 +160,10 @@ theorem formPerm_eq_head_iff_eq_getLast (x y : α) : theorem formPerm_apply_lt_getElem (xs : List α) (h : Nodup xs) (n : ℕ) (hn : n + 1 < xs.length) : formPerm xs xs[n] = xs[n + 1] := by - induction' n with n IH generalizing xs - · simpa using formPerm_apply_getElem_zero _ h _ - · rcases xs with (_ | ⟨x, _ | ⟨y, l⟩⟩) + induction n generalizing xs with + | zero => simpa using formPerm_apply_getElem_zero _ h _ + | succ n IH => + rcases xs with (_ | ⟨x, _ | ⟨y, l⟩⟩) · simp at hn · rw [formPerm_singleton, getElem_singleton, getElem_singleton, one_apply] · specialize IH (y :: l) h.of_cons _ @@ -286,11 +288,13 @@ theorem formPerm_ext_iff {x y x' y' : α} {l l' : List α} (hd : Nodup (x :: y : · rw [length_rotate, hl] · intro k hk hk' rw [getElem_rotate] - induction' k with k IH - · refine Eq.trans ?_ hx' + induction k with + | zero => + refine Eq.trans ?_ hx' congr simpa using hn - · conv => congr <;> · arg 2; (rw [← Nat.mod_eq_of_lt hk']) + | succ k IH => + conv => congr <;> · arg 2; (rw [← Nat.mod_eq_of_lt hk']) rw [← formPerm_apply_getElem _ hd' k (k.lt_succ_self.trans hk'), ← IH (k.lt_succ_self.trans hk), ← h, formPerm_apply_getElem _ hd] congr 1 diff --git a/Mathlib/GroupTheory/Perm/Option.lean b/Mathlib/GroupTheory/Perm/Option.lean index 0b58ddaea3c141..8a127dbe739812 100644 --- a/Mathlib/GroupTheory/Perm/Option.lean +++ b/Mathlib/GroupTheory/Perm/Option.lean @@ -33,7 +33,7 @@ theorem Equiv.optionCongr_sign {α : Type*} [DecidableEq α] [Fintype α] (e : P induction e using Perm.swap_induction_on with | one => simp [Perm.one_def] | swap_mul f x y hne h => - simp [h, hne, Perm.mul_def, ← Equiv.optionCongr_trans] + simp [h, hne, Perm.mul_def] @[simp] theorem map_equiv_removeNone {α : Type*} [DecidableEq α] (σ : Perm (Option α)) : diff --git a/Mathlib/GroupTheory/Perm/Sign.lean b/Mathlib/GroupTheory/Perm/Sign.lean index 9db919eac3d52e..42f9001f127d5e 100644 --- a/Mathlib/GroupTheory/Perm/Sign.lean +++ b/Mathlib/GroupTheory/Perm/Sign.lean @@ -125,14 +125,15 @@ theorem mclosure_swap_castSucc_succ (n : ℕ) : rintro _ ⟨i, j, ne, rfl⟩ wlog lt : i < j generalizing i j · rw [swap_comm]; exact this _ _ ne.symm (ne.lt_or_gt.resolve_left lt) - induction' j using Fin.induction with j ih - · cases lt - have mem : swap j.castSucc j.succ ∈ Submonoid.closure + induction j using Fin.induction with + | zero => cases lt + | succ j ih => + have mem : swap j.castSucc j.succ ∈ Submonoid.closure (Set.range fun (i : Fin n) ↦ swap i.castSucc i.succ) := Submonoid.subset_closure ⟨_, rfl⟩ - obtain rfl | lts := (Fin.le_castSucc_iff.mpr lt).eq_or_lt - · exact mem - rw [swap_comm, ← swap_mul_swap_mul_swap (y := Fin.castSucc j) lts.ne lt.ne] - exact mul_mem (mul_mem mem <| ih lts.ne lts) mem + obtain rfl | lts := (Fin.le_castSucc_iff.mpr lt).eq_or_lt + · exact mem + rw [swap_comm, ← swap_mul_swap_mul_swap (y := Fin.castSucc j) lts.ne lt.ne] + exact mul_mem (mul_mem mem <| ih lts.ne lts) mem /-- Like `swap_induction_on`, but with the composition on the right of `f`. @@ -353,7 +354,7 @@ theorem signAux3_mul_and_swap [Finite α] (f g : Perm α) (s : Multiset α) (hs theorem signAux3_symm_trans_trans [Finite α] [DecidableEq β] [Finite β] (f : Perm α) (e : α ≃ β) {s : Multiset α} {t : Multiset β} (hs : ∀ x, x ∈ s) (ht : ∀ x, x ∈ t) : signAux3 ((e.symm.trans f).trans e) ht = signAux3 f hs := by - induction' t, s using Quotient.inductionOn₂ with t s ht hs + induction t, s using Quotient.inductionOn₂ show signAux2 _ _ = signAux2 _ _ rcases Finite.exists_equiv_fin β with ⟨n, ⟨e'⟩⟩ rw [← signAux_eq_signAux2 _ _ e' fun _ _ => ht _, @@ -513,20 +514,22 @@ theorem prod_prodExtendRight {α : Type*} [DecidableEq α] (σ : α → Perm β) obtain ⟨_, prod_eq⟩ := Or.resolve_right this (not_and.mpr fun h _ => h (mem_l a)) rw [prod_eq, prodCongrRight_apply] clear mem_l - induction' l with a' l ih - · refine Or.inr ⟨List.not_mem_nil, ?_⟩ + induction l with + | nil => + refine Or.inr ⟨List.not_mem_nil, ?_⟩ rw [List.map_nil, List.prod_nil, one_apply] - rw [List.map_cons, List.prod_cons, mul_apply] - rcases ih (List.nodup_cons.mp hl).2 with (⟨mem_l, prod_eq⟩ | ⟨notMem_l, prod_eq⟩) <;> - rw [prod_eq] - · refine Or.inl ⟨List.mem_cons_of_mem _ mem_l, ?_⟩ - rw [prodExtendRight_apply_ne _ fun h : a = a' => (List.nodup_cons.mp hl).1 (h ▸ mem_l)] - by_cases ha' : a = a' - · rw [← ha'] at * - refine Or.inl ⟨l.mem_cons_self, ?_⟩ - rw [prodExtendRight_apply_eq] - · refine Or.inr ⟨fun h => not_or_intro ha' notMem_l ((List.mem_cons).mp h), ?_⟩ - rw [prodExtendRight_apply_ne _ ha'] + | cons a' l ih => + rw [List.map_cons, List.prod_cons, mul_apply] + rcases ih (List.nodup_cons.mp hl).2 with (⟨mem_l, prod_eq⟩ | ⟨notMem_l, prod_eq⟩) <;> + rw [prod_eq] + · refine Or.inl ⟨List.mem_cons_of_mem _ mem_l, ?_⟩ + rw [prodExtendRight_apply_ne _ fun h : a = a' => (List.nodup_cons.mp hl).1 (h ▸ mem_l)] + by_cases ha' : a = a' + · rw [← ha'] at * + refine Or.inl ⟨l.mem_cons_self, ?_⟩ + rw [prodExtendRight_apply_eq] + · refine Or.inr ⟨fun h => not_or_intro ha' notMem_l ((List.mem_cons).mp h), ?_⟩ + rw [prodExtendRight_apply_ne _ ha'] section congr diff --git a/Mathlib/GroupTheory/Perm/Support.lean b/Mathlib/GroupTheory/Perm/Support.lean index 6518974824da5e..9ede7900c62539 100644 --- a/Mathlib/GroupTheory/Perm/Support.lean +++ b/Mathlib/GroupTheory/Perm/Support.lean @@ -113,9 +113,10 @@ theorem Disjoint.conj (H : Disjoint f g) (h : Perm α) : Disjoint (h * f * h⁻ theorem disjoint_prod_right (l : List (Perm α)) (h : ∀ g ∈ l, Disjoint f g) : Disjoint f l.prod := by - induction' l with g l ih - · exact disjoint_one_right _ - · rw [List.prod_cons] + induction l with + | nil => exact disjoint_one_right _ + | cons g l ih => + rw [List.prod_cons] exact (h _ List.mem_cons_self).mul_right (ih fun g hg => h g (List.mem_cons_of_mem _ hg)) theorem disjoint_noncommProd_right {ι : Type*} {k : ι → Perm α} {s : Finset ι} @@ -320,9 +321,10 @@ theorem exists_mem_support_of_mem_support_prod {l : List (Perm α)} {x : α} (hx : x ∈ l.prod.support) : ∃ f : Perm α, f ∈ l ∧ x ∈ f.support := by contrapose! hx simp_rw [mem_support, not_not] at hx ⊢ - induction' l with f l ih - · rfl - · rw [List.prod_cons, mul_apply, ih, hx] + induction l with + | nil => rfl + | cons f l ih => + rw [List.prod_cons, mul_apply, ih, hx] · simp only [List.find?, List.mem_cons, true_or] intros f' hf' refine hx f' ?_ @@ -402,9 +404,10 @@ theorem zpow_apply_mem_support {n : ℤ} {x : α} : (f ^ n) x ∈ f.support ↔ theorem pow_eq_on_of_mem_support (h : ∀ x ∈ f.support ∩ g.support, f x = g x) (k : ℕ) : ∀ x ∈ f.support ∩ g.support, (f ^ k) x = (g ^ k) x := by - induction' k with k hk - · simp - · intro x hx + induction k with + | zero => simp + | succ k hk => + intro x hx rw [pow_succ, mul_apply, pow_succ, mul_apply, h _ hx, hk] rwa [mem_inter, apply_mem_support, ← h _ hx, apply_mem_support, ← mem_inter] @@ -424,9 +427,10 @@ theorem Disjoint.support_mul (h : Disjoint f g) : (f * g).support = f.support theorem support_prod_of_pairwise_disjoint (l : List (Perm α)) (h : l.Pairwise Disjoint) : l.prod.support = (l.map support).foldr (· ⊔ ·) ⊥ := by - induction' l with hd tl hl - · simp - · rw [List.pairwise_cons] at h + induction l with + | nil => simp + | cons hd tl hl => + rw [List.pairwise_cons] at h have : Disjoint hd tl.prod := disjoint_prod_right _ h.left simp [this.support_mul, hl h.right] @@ -448,9 +452,10 @@ theorem support_noncommProd {ι : Type*} {k : ι → Perm α} {s : Finset ι} simp only [Finset.coe_insert, Set.mem_insert_iff, Finset.mem_coe, hj, or_true, true_or] theorem support_prod_le (l : List (Perm α)) : l.prod.support ≤ (l.map support).foldr (· ⊔ ·) ⊥ := by - induction' l with hd tl hl - · simp - · rw [List.prod_cons, List.map_cons, List.foldr_cons] + induction l with + | nil => simp + | cons hd tl hl => + rw [List.prod_cons, List.map_cons, List.foldr_cons] refine (support_mul_le hd tl.prod).trans ?_ exact sup_le_sup le_rfl hl @@ -523,9 +528,10 @@ theorem Disjoint.mem_imp (h : Disjoint f g) {x : α} (hx : x ∈ f.support) : x theorem eq_on_support_mem_disjoint {l : List (Perm α)} (h : f ∈ l) (hl : l.Pairwise Disjoint) : ∀ x ∈ f.support, f x = l.prod x := by - induction' l with hd tl IH - · simp at h - · intro x hx + induction l with + | nil => simp at h + | cons hd tl IH => + intro x hx rw [List.pairwise_cons] at hl rw [List.mem_cons] at h rcases h with (rfl | h) @@ -643,9 +649,10 @@ theorem Disjoint.card_support_mul (h : Disjoint f g) : theorem card_support_prod_list_of_pairwise_disjoint {l : List (Perm α)} (h : l.Pairwise Disjoint) : #l.prod.support = (l.map (card ∘ support)).sum := by - induction' l with a t ih - · exact card_support_eq_zero.mpr rfl - · obtain ⟨ha, ht⟩ := List.pairwise_cons.1 h + induction l with + | nil => exact card_support_eq_zero.mpr rfl + | cons a t ih => + obtain ⟨ha, ht⟩ := List.pairwise_cons.1 h rw [List.prod_cons, List.map_cons, List.sum_cons, ← ih ht] exact (disjoint_prod_right _ ha).card_support_mul diff --git a/Mathlib/GroupTheory/PresentedGroup.lean b/Mathlib/GroupTheory/PresentedGroup.lean index 1eefe27652739b..2fa42510a6b85e 100644 --- a/Mathlib/GroupTheory/PresentedGroup.lean +++ b/Mathlib/GroupTheory/PresentedGroup.lean @@ -85,7 +85,7 @@ theorem induction_on {rels : Set (FreeGroup α)} {C : PresentedGroup rels → Pr theorem generated_by (rels : Set (FreeGroup α)) (H : Subgroup (PresentedGroup rels)) (h : ∀ j : α, PresentedGroup.of j ∈ H) (x : PresentedGroup rels) : x ∈ H := by - induction' x with z + obtain ⟨z⟩ := x induction z · exact one_mem H · exact h _ diff --git a/Mathlib/GroupTheory/SemidirectProduct.lean b/Mathlib/GroupTheory/SemidirectProduct.lean index d16ef08dc87d92..32dae6a4738ebf 100644 --- a/Mathlib/GroupTheory/SemidirectProduct.lean +++ b/Mathlib/GroupTheory/SemidirectProduct.lean @@ -178,8 +178,6 @@ theorem range_inl_eq_ker_rightHom : (inl : N →* N ⋊[φ] G).range = rightHom. def equivProd : N ⋊[φ] G ≃ N × G where toFun x := ⟨x.1, x.2⟩ invFun x := ⟨x.1, x.2⟩ - left_inv _ := rfl - right_inv _ := rfl /-- The group isomorphism between a semidirect product with respect to the trivial map and the product. -/ diff --git a/Mathlib/GroupTheory/Solvable.lean b/Mathlib/GroupTheory/Solvable.lean index f438f357e7b586..2f936606e6a403 100644 --- a/Mathlib/GroupTheory/Solvable.lean +++ b/Mathlib/GroupTheory/Solvable.lean @@ -124,10 +124,11 @@ theorem solvable_of_ker_le_range {G' G'' : Type*} [Group G'] [Group G''] (f : G' obtain ⟨m, hm⟩ := id hG' refine ⟨⟨n + m, le_bot_iff.mp (Subgroup.map_bot f ▸ hm ▸ ?_)⟩⟩ clear hm - induction' m with m hm - · exact f.range_eq_map ▸ ((derivedSeries G n).map_eq_bot_iff.mp + induction m with + | zero => + exact f.range_eq_map ▸ ((derivedSeries G n).map_eq_bot_iff.mp (le_bot_iff.mp ((map_derivedSeries_le_derivedSeries g n).trans hn.le))).trans hfg - · exact commutator_le_map_commutator hm hm + | succ m hm => exact commutator_le_map_commutator hm hm theorem solvable_of_solvable_injective (hf : Function.Injective f) [IsSolvable G'] : IsSolvable G := @@ -155,9 +156,9 @@ theorem IsSolvable.commutator_lt_top_of_nontrivial [hG : IsSolvable G] [Nontrivi obtain ⟨n, hn⟩ := hG contrapose! hn refine ne_of_eq_of_ne ?_ top_ne_bot - induction' n with n h - · exact derivedSeries_zero G - · rwa [derivedSeries_succ, h] + induction n with + | zero => exact derivedSeries_zero G + | succ n h => rwa [derivedSeries_succ, h] theorem IsSolvable.commutator_lt_of_ne_bot [IsSolvable G] {H : Subgroup G} (hH : H ≠ ⊥) : ⁅H, H⁆ < H := by @@ -178,10 +179,11 @@ theorem isSolvable_iff_commutator_lt [WellFoundedLT (Subgroup G)] : rw [← (map_injective (subtype_injective _)).eq_iff, Subgroup.map_bot] at hn ⊢ rw [← hn] clear hn - induction' n with n ih - · rw [derivedSeries_succ, derivedSeries_zero, derivedSeries_zero, map_commutator, + induction n with + | zero => + rw [derivedSeries_succ, derivedSeries_zero, derivedSeries_zero, map_commutator, ← MonoidHom.range_eq_map, ← MonoidHom.range_eq_map, range_subtype, range_subtype] - · rw [derivedSeries_succ, map_commutator, ih, derivedSeries_succ, map_commutator] + | succ n ih => rw [derivedSeries_succ, map_commutator, ih, derivedSeries_succ, map_commutator] end Solvable diff --git a/Mathlib/GroupTheory/SpecificGroups/Alternating.lean b/Mathlib/GroupTheory/SpecificGroups/Alternating.lean index 15003ab1be1640..62cfbba52c1728 100644 --- a/Mathlib/GroupTheory/SpecificGroups/Alternating.lean +++ b/Mathlib/GroupTheory/SpecificGroups/Alternating.lean @@ -166,8 +166,9 @@ theorem closure_three_cycles_eq_alternating : rw [← two_mul] at hn exact hind n l hl hn intro n - induction' n with n ih <;> intro l hl hn - · simp [List.length_eq_zero_iff.1 hn, one_mem] + induction n with intro l hl hn + | zero => simp [List.length_eq_zero_iff.1 hn, one_mem] + | succ n ih => rw [Nat.mul_succ] at hn obtain ⟨a, l, rfl⟩ := l.exists_of_length_succ hn rw [List.length_cons, Nat.succ_inj] at hn diff --git a/Mathlib/GroupTheory/SpecificGroups/Cyclic.lean b/Mathlib/GroupTheory/SpecificGroups/Cyclic.lean index f7de4d901376ff..11e8ae1e4d640f 100644 --- a/Mathlib/GroupTheory/SpecificGroups/Cyclic.lean +++ b/Mathlib/GroupTheory/SpecificGroups/Cyclic.lean @@ -464,7 +464,7 @@ open Nat @[to_additive] private theorem card_orderOf_eq_totient_aux₁ {d : ℕ} (hd : d ∣ Fintype.card α) (hpos : 0 < #{a : α | orderOf a = d}) : #{a : α | orderOf a = d} = φ d := by - induction' d using Nat.strongRec' with d IH + induction d using Nat.strongRec' with | _ d IH rcases Decidable.eq_or_ne d 0 with (rfl | hd0) · cases Fintype.card_ne_zero (eq_zero_of_zero_dvd hd) rcases Finset.card_pos.1 hpos with ⟨a, ha'⟩ diff --git a/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean b/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean index 00b5393d971324..23e0831d2acf6d 100644 --- a/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean +++ b/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean @@ -166,9 +166,10 @@ theorem card [NeZero n] : Fintype.card (QuaternionGroup n) = 4 * n := by @[simp] theorem a_one_pow (k : ℕ) : (a 1 : QuaternionGroup n) ^ k = a k := by - induction' k with k IH - · rw [Nat.cast_zero]; rfl - · rw [pow_succ, IH, a_mul_a] + induction k with + | zero => rw [Nat.cast_zero]; rfl + | succ k IH => + rw [pow_succ, IH, a_mul_a] congr 1 norm_cast diff --git a/Mathlib/GroupTheory/Submonoid/Center.lean b/Mathlib/GroupTheory/Submonoid/Center.lean index addceff4248907..a1d16b799d4d3a 100644 --- a/Mathlib/GroupTheory/Submonoid/Center.lean +++ b/Mathlib/GroupTheory/Submonoid/Center.lean @@ -173,8 +173,6 @@ def Submonoid.centerCongr [MulOneClass M] [MulOneClass N] (e : M ≃* N) : cente def Subsemigroup.centerToMulOpposite [Mul M] : center M ≃* center Mᵐᵒᵖ where toFun r := ⟨_, MulOpposite.op_mem_center_iff.mpr r.2⟩ invFun r := ⟨_, MulOpposite.unop_mem_center_iff.mpr r.2⟩ - left_inv _ := rfl - right_inv _ := rfl map_mul' r _ := Subtype.ext (congr_arg MulOpposite.op <| r.2.1 _) /-- The center of a monoid is isomorphic to the center of its opposite. -/ diff --git a/Mathlib/GroupTheory/Torsion.lean b/Mathlib/GroupTheory/Torsion.lean index 49a039de16c305..1eda811f7cf550 100644 --- a/Mathlib/GroupTheory/Torsion.lean +++ b/Mathlib/GroupTheory/Torsion.lean @@ -397,7 +397,7 @@ variable (G) [CommGroup G] "Quotienting a group by its additive torsion subgroup yields an additive torsion-free group."] instance _root_.QuotientGroup.instIsMulTorsionFree : IsMulTorsionFree <| G ⧸ torsion G := by refine .of_not_isOfFinOrder fun g hne hfin ↦ hne ?_ - induction' g using QuotientGroup.induction_on with g + obtain ⟨g⟩ := g obtain ⟨m, mpos, hm⟩ := hfin.exists_pow_eq_one obtain ⟨n, npos, hn⟩ := ((QuotientGroup.eq_one_iff _).mp hm).exists_pow_eq_one exact (QuotientGroup.eq_one_iff g).mpr @@ -408,7 +408,7 @@ instance _root_.QuotientGroup.instIsMulTorsionFree : IsMulTorsionFree <| G ⧸ t "Quotienting a group by its additive torsion subgroup yields an additive torsion free group."] theorem IsTorsionFree.quotient_torsion : IsTorsionFree <| G ⧸ torsion G := fun g hne hfin => hne <| by - induction' g using QuotientGroup.induction_on with g + obtain ⟨g⟩ := g obtain ⟨m, mpos, hm⟩ := hfin.exists_pow_eq_one obtain ⟨n, npos, hn⟩ := ((QuotientGroup.eq_one_iff _).mp hm).exists_pow_eq_one exact diff --git a/Mathlib/Init.lean b/Mathlib/Init.lean index bcc3631efc7ec4..7aee07352925c7 100644 --- a/Mathlib/Init.lean +++ b/Mathlib/Init.lean @@ -78,3 +78,21 @@ register_linter_set linter.mathlibStandardSet := linter.style.setOption linter.style.maxHeartbeats -- The `docPrime` linter is disabled: https://github.com/leanprover-community/mathlib4/issues/20560 + +-- Check that all linter options mentioned in the mathlib standard linter set exist. +open Lean Elab.Command Linter Mathlib.Linter Mathlib.Linter.Style + +run_cmd liftTermElabM do + let DefinedInScripts : Array Name := + #[`linter.checkInitImports, `linter.allScriptsDocumented] + let env ← getEnv + let ls := linterSetsExt.getEntries env + let some (_, mlLinters) := ls.find? (·.1 == ``linter.mathlibStandardSet) | + throwError m!"'linter.mathlibStandardSet' is not defined." + for mll in mlLinters do + let [(mlRes, _)] ← realizeGlobalName mll | + if !DefinedInScripts.contains mll then + throwError "Unknown option '{mll}'!" + let some cinfo := env.find? mlRes | throwError "{mlRes}: this code should be unreachable." + if !cinfo.type.isAppOf ``Lean.Option then + throwError "{.ofConstName mlRes} is not an option, it is a{indentD cinfo.type}" diff --git a/Mathlib/Lean/Meta/RefinedDiscrTree.lean b/Mathlib/Lean/Meta/RefinedDiscrTree.lean index ecb15d2661d089..5ff82815224122 100644 --- a/Mathlib/Lean/Meta/RefinedDiscrTree.lean +++ b/Mathlib/Lean/Meta/RefinedDiscrTree.lean @@ -1,223 +1,153 @@ /- -Copyright (c) 2023 J. W. Gerbscheid. All rights reserved. +Copyright (c) 2024 Jovan Gerbscheid. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: J. W. Gerbscheid +Authors: Jovan Gerbscheid -/ -import Mathlib.Lean.Meta.RefinedDiscrTree.Pi -import Mathlib.Lean.Meta.RefinedDiscrTree.Basic -import Mathlib.Lean.Meta.RefinedDiscrTree.Encode +import Mathlib.Lean.Meta.RefinedDiscrTree.Lookup +import Mathlib.Lean.Meta.RefinedDiscrTree.Initialize /-! -We define discrimination trees for the purpose of unifying local expressions with library results. +A discrimination tree for the purpose of unifying local expressions with library results. -This structure is based on `Lean.Meta.DiscrTree`. -I document here what features are not in the original: +This data structure is based on `Lean.Meta.DiscrTree` and `Lean.Meta.LazyDiscrTree`, +and includes many more features. + +## New features - The keys `Key.lam`, `Key.forall` and `Key.bvar` have been introduced in order to allow for matching under lambda and forall binders. `Key.lam` has arity 1 and indexes the body. `Key.forall` has arity 2 and indexes the domain and the body. The reason for not indexing the domain of a lambda expression is that it is usually already determined, for example in `∃ a : α, p`, which is `@Exists α fun a : α => p`, we don't want to index the domain `α` twice. - In a forall expression it is necessary to index the domain, because in an implication `p → q` + In a forall expression we should index the domain, because in an implication `p → q` we need to index both `p` and `q`. `Key.bvar` works the same as `Key.fvar`, but stores the - De Bruijn index to identify it. + De Bruijn index to identify the variable. For example, this allows for more specific matching with the left hand side of - `∑ i ∈ range n, i = n * (n - 1) / 2`, which is indexed by + `∑ i ∈ Finset.range n, i = n * (n - 1) / 2`, which is indexed by `[⟨Finset.sum, 5⟩, ⟨Nat, 0⟩, ⟨Nat, 0⟩, *0, ⟨Finset.Range, 1⟩, *1, λ, ⟨#0, 0⟩]`. - The key `Key.star` takes a `Nat` identifier as an argument. For example, - the library pattern `?a + ?a` is encoded as `[⟨HAdd.hAdd, 6⟩, *0, *0, *0, *1, *2, *2]`. - `*0` corresponds to the type of `a`, `*1` to the `HAdd` instance, and `*2` to `a`. - This means that it will only match an expression `x + y` if `x` is definitionally equal to `y`. - The matching algorithm requires that the same stars from the discrimination tree match with - the same patterns in the lookup expression, and similarly requires that the same metavariables - form the lookup expression match with the same pattern in the discrimination tree. - -- The key `Key.opaque` has been introduced in order to index existential variables - in lemmas like `Nat.exists_prime_and_dvd {n : ℕ} (hn : n ≠ 1) : ∃ p, Prime p ∧ p ∣ n`, - where the part `Prime p` gets the pattern `[⟨Nat.Prime, 1⟩, ◾]`. (◾ represents `Key.opaque`) - When matching, `Key.opaque` can only be matched by `Key.star`. - - Using the `WhnfCoreConfig` argument, it is possible to disable β-reduction and ζ-reduction. - As a result, we may get a lambda expression applied to an argument or a let-expression. - Since there is no support for indexing these, they will be indexed by `Key.opaque`. - -- We keep track of the matching score of a unification. + the library pattern `?a + ?a` is encoded as `@HAdd.hAdd *0 *0 *1 *2 *3 *3`. + `*0` corresponds to the type of `?a`, `*1` to the outParam of `HAdd.hAdd`, + `*2` to the `HAdd` instance, and `*3` to `a`. This means that it will only match an expression + `x + y` if `x` is indexed the same as `y`. The matching algorithm ensures that both + instances of `*3` match with the same pattern in the lookup expression. + +- We evaluate the matching score of a unification. This score represents the number of keys that had to be the same for the unification to succeed. For example, matching `(1 + 2) + 3` with `add_comm` gives a score of 2, - since the pattern of commutativity is [⟨HAdd.hAdd, 6⟩, *0, *0, *0, *1, *2, *3], - so matching `⟨HAdd.hAdd, 6⟩` gives 1 point, - and matching `*0` after its first appearance gives another point, but the third argument is an - outParam, so this gets ignored. Similarly, matching it with `add_assoc` gives a score of 5. + since the pattern of `add_comm` is `@HAdd.hAdd *0 *0 *0 *1 *2 *3`: matching `HAdd.hAdd` + gives 1 point, and matching `*0` again after its first appearence gives another point. + Similarly, matching it with `Nat.add_comm` gives a score of 3, and `add_assoc` gives a score of 5. - Patterns that have the potential to be η-reduced are put into the `RefinedDiscrTree` under all possible reduced key sequences. This is for terms of the form `fun x => f (?m x₁ .. xₙ)`, where - `?m` is a metavariable, and one of `x₁, .., xₙ` in `x`. + `?m` is a metavariable, one of `x₁, .., xₙ` is `x`, and `f` is not a metavariable. For example, the pattern `Continuous fun y => Real.exp (f y)])` is indexed by - both `[⟨Continuous, 5⟩, *0, ⟨Real, 0⟩, *1, *2, λ, ⟨Real.exp⟩, *3]` - and `[⟨Continuous, 5⟩, *0, ⟨Real, 0⟩, *1, *2, ⟨Real.exp⟩]` - so that it also comes up if you search with `Continuous Real.exp`. - Similarly, `Continuous fun x => f x + g x` is indexed by - both `[⟨Continuous, 1⟩, λ, ⟨HAdd.hAdd, 6⟩, *0, *0, *0, *1, *2, *3]` - and `[⟨Continuous, 1⟩, ⟨HAdd.hAdd, 5⟩, *0, *0, *0, *1, *2]`. - -- For sub-expressions not at the root of the original expression we have some additional reductions: - - Any combination of `ofNat`, `Nat.zero`, `Nat.succ` and number literals - is stored as just a number literal. - - The expression `fun a : α => a` is stored as `@id α`. - - This makes lemmata such as `continuous_id'` redundant, which is the same as `continuous_id`, - with `id` replaced by `fun x => x`. - - Any expressions involving `+`, `*`, `-`, `/` or `⁻¹` is normalized to not have a lambda - in front and to always have the default amount of arguments. - e.g. `(f + g) a` is stored as `f a + g a` and `fun x => f x + g x` is stored as `f + g`. - - This makes lemmata such as `MeasureTheory.integral_integral_add'` redundant, which is the - same as `MeasureTheory.integral_integral_add`, with `f a + g a` replaced by `(f + g) a` - - it also means that a lemma like `Continuous.mul` can be stated as talking about `f * g` - instead of `fun x => f x + g x`. - -I have also made some changes in the implementation: - -- Instead of directly converting from `Expr` to `Array Key` during insertion, and directly - looking up from an `Expr` during lookup, I defined the intermediate structure `DTExpr`, - which is a form of `Expr` that only contains information relevant for the discrimination tree. - Each `Expr` is transformed into a `DTExpr` before insertion or lookup. For insertion there - could be multiple `DTExpr` representations due to potential η-reductions as mentioned above. - -TODO: - -- More thought could be put into the matching algorithm for non-trivial unifications. - For example, when looking up the expression `?a + ?a` (for rewriting), there will only be - results like `n + n = 2 * n` or `a + b = b + a`, but not like `n + 1 = n.succ`, - even though this would still unify. - -- The reason why implicit arguments are not ignored by the discrimination tree is that they provide - important type information. Because of this it seems more natural to index the types of - expressions instead of indexing the implicit type arguments. Then each key would additionally - index the type of that expression. So instead of indexing `?a + ?b` as - `[⟨HAdd.hAdd, 6⟩, *0, *0, *0, *1, *2, *3]`, it would be indexed by something like - `[(*0, ⟨HAdd.hAdd, 6⟩), _, _, _, _, (*0, *1), (*0, *2)]`. - The advantage of this would be that there will be less duplicate indexing of types, - because many functions index the types of their arguments and their return type - with implicit arguments, meaning that types unnecessarily get indexed multiple times. - This modification can be explored, but it could very well not be an improvement. + both `@Continuous *0 ℝ *1 *2 (λ, Real.exp *3)` + and `@Continuous *0 ℝ *1 *2 Real.exp`, + so that it also comes up if you look up `Continuous Real.exp`. --/ +- How to deal with number literals is waiting for this issue to be resolved: + https://github.com/leanprover/lean4/issues/2867 -open Lean Meta +- The key `Key.opaque` only matches with a `Key.star` key. + Depending on the configuration, β-reduction and ζ-reduction may be disabled, so the resulting + applied lambda expressions or let-expressions are indexed by `Key.opaque`. -namespace Lean.Meta.RefinedDiscrTree -variable {α} -/-! ## Inserting intro a RefinedDiscrTree -/ +## Lazy computation + +To encode an `Expr` as a sequence of `Key`s, we start with a `LazyEntry` and +we have a incremental evaluation function of type +`LazyEntry → MetaM (Option (List (Key × LazyEntry)))`, which computes the next keys +and lazy entries, or returns `none` if the last key has been reached already. + +The `RefinedDiscrTree` then stores these `LazyEntries` at its leafs, and evaluates them +only if the lookup algorithm reaches this leaf. + + +## Alternative optimizations + +`RefinedDiscrTree` is a non-persistent lazy data-structure. Therefore, when using it, you should +try to use it linearly (i.e. having reference count 1). This is ideal for library search purposes, +which build the discrimination tree once, and store a reference to the tree. + +However, for tactics like `simp` and `fun_prop` this is less ideal, because they can't use the +data-structure linearly, since copies of the data structure must regularly be stored in the +environment. For `fun_prop` this is not a serious problem since it doesn't have that many +different lemmas anyways. + +#### Future work: +Make a version of `RefinedDiscrTree` that is optimal for tactics like `simp` and +`fun_prop`. This would mean using a persistent data structure, and possibly a non-lazy strcture. + + +## Matching vs Unification + +A discrimination tree can be used in two ways: either with (unification) or without (matching) +allowing metavariables in the target expression to be instantiated. +Most applications use matching, and the only common use case where unification is used is +type class search. Since the intended applications of the `RefinedDiscrTree` currently use +matching, the lookup algorithm is most optimized for matching. + +#### Future work: +Improve the unification lookup. + +-/ + +namespace Lean.Meta.RefinedDiscrTree -/-- If `vs` contains an element `v'` such that `v == v'`, then replace `v'` with `v`. -Otherwise, push `v`. -See issue https://github.com/leanprover-community/mathlib4/pull/2155 -Recall that `BEq α` may not be Lawful. +variable {α : Type} + +/-- Creates the core context used for initializing a tree using the current context. -/ +private def withTreeCtx (ctx : Core.Context) : Core.Context := + { ctx with maxHeartbeats := 0, diag := getDiag ctx.options } + +/-- Returns candidates from all imported modules that match the expression. -/ +def findImportMatches + (ext : EnvExtension (IO.Ref (Option (RefinedDiscrTree α)))) + (addEntry : Name → ConstantInfo → MetaM (List (α × List (Key × LazyEntry)))) (ty : Expr) + (constantsPerTask : Nat := 1000) (capacityPerTask : Nat := 128) : MetaM (MatchResult α) := do + let ngen ← getNGen + let (cNGen, ngen) := ngen.mkChild + setNGen ngen + let _ : Inhabited (IO.Ref (Option (RefinedDiscrTree α))) := ⟨← IO.mkRef none⟩ + let ref := EnvExtension.getState ext (← getEnv) + let importTree ← (← ref.get).getDM do + profileitM Exception "RefinedDiscrTree import initialization" (← getOptions) <| + withTheReader Core.Context withTreeCtx <| + createImportedDiscrTree cNGen (← getEnv) addEntry constantsPerTask capacityPerTask + let (importCandidates, importTree) ← getMatch importTree ty false false + ref.set (some importTree) + return importCandidates + +/-- Returns candidates from this module that match the expression. -/ +def findModuleMatches (moduleRef : ModuleDiscrTreeRef α) (ty : Expr) : MetaM (MatchResult α) := do + profileitM Exception "RefinedDiscrTree local search" (← getOptions) do + let discrTree ← moduleRef.ref.get + let (localCandidates, localTree) ← getMatch discrTree ty false false + moduleRef.ref.set localTree + return localCandidates + +/-- +`findMatches` combines `findImportMatches` and `findModuleMatches`. + +* `ext` should be an environment extension with an `IO.Ref` for caching the `RefinedDiscrTree`. +* `addEntry` is the function for creating `RefinedDiscrTree` entries from constants. +* `ty` is the expression type. +* `constantsPerTask` is the number of constants in imported modules to be used for each + new task. +* `capacityPerTask` is the initial capacity of the `HashMap` at the root of the + `RefinedDiscrTree` for each new task. -/ -private def insertInArray [BEq α] (vs : Array α) (v : α) : Array α := - loop 0 -where - loop (i : Nat) : Array α := - if h : i < vs.size then - if v == vs[i] then - vs.set i v - else - loop (i+1) - else - vs.push v - -/-- Insert the value `v` at index `keys : Array Key` in a `Trie`. -/ -partial def insertInTrie [BEq α] (keys : Array Key) (v : α) (i : Nat) : Trie α → Trie α - | .node cs => - let k := keys[i]! - let c := Id.run <| cs.binInsertM - (fun a b => a.1 < b.1) - (fun (k', s) => (k', insertInTrie keys v (i+1) s)) - (fun _ => (k, Trie.singleton keys v (i+1))) - (k, default) - .node c - | .values vs => - .values (insertInArray vs v) - | .path ks c => Id.run do - for h : n in [:ks.size] do - let k1 := keys[i+n]! - let k2 := ks[n] - if k1 != k2 then - let shared := ks[:n] - let rest := ks[n+1:] - return .mkPath shared (.mkNode2 k1 (.singleton keys v (i+n+1)) k2 (.mkPath rest c)) - return .path ks (insertInTrie keys v (i + ks.size) c) - -/-- Insert the value `v` at index `keys : Array Key` in a `RefinedDiscrTree`. - -Warning: to account for η-reduction, an entry may need to be added at multiple indexes, -so it is recommended to use `RefinedDiscrTree.insert` for insertion. -/ -def insertInRefinedDiscrTree [BEq α] (d : RefinedDiscrTree α) (keys : Array Key) (v : α) : - RefinedDiscrTree α := - let k := keys[0]! - match d.root.find? k with - | none => - let c := .singleton keys v 1 - { root := d.root.insert k c } - | some c => - let c := insertInTrie keys v 1 c - { root := d.root.insert k c } - -/-- Insert the value `v` at index `e : DTExpr` in a `RefinedDiscrTree`. - -Warning: to account for η-reduction, an entry may need to be added at multiple indexes, -so it is recommended to use `RefinedDiscrTree.insert` for insertion. -/ -def insertDTExpr [BEq α] (d : RefinedDiscrTree α) (e : DTExpr) (v : α) : RefinedDiscrTree α := - insertInRefinedDiscrTree d e.flatten v - -/-- Insert the value `v` at index `e : Expr` in a `RefinedDiscrTree`. -The argument `fvarInContext` allows you to specify which free variables in `e` will still be -in the context when the `RefinedDiscrTree` is being used for lookup. -It should return true only if the `RefinedDiscrTree` is built and used locally. - -if `onlySpecific := true`, then we filter out the patterns `*` and `Eq * * *`. -/ -def insert [BEq α] (d : RefinedDiscrTree α) (e : Expr) (v : α) - (onlySpecific : Bool := true) (fvarInContext : FVarId → Bool := fun _ => false) : - MetaM (RefinedDiscrTree α) := do - let keys ← mkDTExprs e onlySpecific fvarInContext - return keys.foldl (insertDTExpr · · v) d - -/-- Insert the value `vLhs` at index `lhs`, and if `rhs` is indexed differently, then also -insert the value `vRhs` at index `rhs`. -/ -def insertEqn [BEq α] (d : RefinedDiscrTree α) (lhs rhs : Expr) (vLhs vRhs : α) - (onlySpecific : Bool := true) (fvarInContext : FVarId → Bool := fun _ => false) : - MetaM (RefinedDiscrTree α) := do - let keysLhs ← mkDTExprs lhs onlySpecific fvarInContext - let keysRhs ← mkDTExprs rhs onlySpecific fvarInContext - let d := keysLhs.foldl (insertDTExpr · · vLhs) d - if @List.beq _ ⟨DTExpr.eqv⟩ keysLhs keysRhs then - return d - else - return keysRhs.foldl (insertDTExpr · · vRhs) d - - - -variable {β : Type} {m : Type → Type} [Monad m] - -/-- Apply a monadic function to the array of values at each node in a `RefinedDiscrTree`. -/ -partial def Trie.mapArraysM (t : RefinedDiscrTree.Trie α) (f : Array α → m (Array β)) : - m (Trie β) := do - match t with - | .node children => - return .node (← children.mapM fun (k, t') => do pure (k, ← t'.mapArraysM f)) - | .values vs => - return .values (← f vs) - | .path ks c => - return .path ks (← c.mapArraysM f) - -/-- Apply a monadic function to the array of values at each node in a `RefinedDiscrTree`. -/ -def mapArraysM (d : RefinedDiscrTree α) (f : Array α → m (Array β)) : m (RefinedDiscrTree β) := - return { root := ← d.root.mapM (·.mapArraysM f) } - -/-- Apply a function to the array of values at each node in a `RefinedDiscrTree`. -/ -def mapArrays (d : RefinedDiscrTree α) (f : Array α → Array β) : RefinedDiscrTree β := - d.mapArraysM (m := Id) f +def findMatches (ext : EnvExtension (IO.Ref (Option (RefinedDiscrTree α)))) + (addEntry : Name → ConstantInfo → MetaM (List (α × List (Key × LazyEntry)))) + (ty : Expr) (constantsPerTask : Nat := 1000) (capacityPerTask : Nat := 128) : + MetaM (MatchResult α × MatchResult α) := do + let moduleMatches ← findModuleMatches (← createModuleTreeRef addEntry) ty + let importMatches ← findImportMatches ext addEntry ty constantsPerTask capacityPerTask + return (moduleMatches, importMatches) end Lean.Meta.RefinedDiscrTree diff --git a/Mathlib/Lean/Meta/RefinedDiscrTree/Basic.lean b/Mathlib/Lean/Meta/RefinedDiscrTree/Basic.lean index b8f2d6390d502f..549b21bc2b7b2f 100644 --- a/Mathlib/Lean/Meta/RefinedDiscrTree/Basic.lean +++ b/Mathlib/Lean/Meta/RefinedDiscrTree/Basic.lean @@ -1,5 +1,5 @@ /- -Copyright (c) 2023 Jovan Gerbscheid. All rights reserved. +Copyright (c) 2024 Jovan Gerbscheid. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Jovan Gerbscheid -/ @@ -7,172 +7,312 @@ import Mathlib.Init /-! # Basic Definitions for `RefinedDiscrTree` + +We define +* `Key`, the discrimination tree key +* `LazyEntry`, the partial, lazy computation of a sequence of `Key`s +* `Trie`, a node of the discrimination tree, which is indexed with `Key`s + and stores an array of pending `LazyEntry`s +* `RefinedDiscrTree`, the discrimination tree itself. -/ namespace Lean.Meta.RefinedDiscrTree -/-! ## Definitions -/ /-- Discrimination tree key. -/ inductive Key where - /-- A metavariable. This key matches with anything. It stores an index. -/ - | star : Nat → Key - /-- An opaque variable. This key only matches with itself or `Key.star`. -/ - | opaque : Key + /-- A metavariable. This key matches with anything. -/ + | star + /-- A metavariable. This key matches with anything. It stores an identifier. -/ + | labelledStar (id : Nat) + /-- An opaque variable. This key only matches with `Key.star`. -/ + | opaque /-- A constant. It stores the name and the arity. -/ - | const : Name → Nat → Key + | const (declName : Name) (nargs : Nat) /-- A free variable. It stores the `FVarId` and the arity. -/ - | fvar : FVarId → Nat → Key + | fvar (fvarId : FVarId) (nargs : Nat) /-- A bound variable, from a lambda or forall binder. It stores the De Bruijn index and the arity. -/ - | bvar : Nat → Nat → Key + | bvar (deBruijnIndex nargs : Nat) /-- A literal. -/ - | lit : Literal → Key + | lit (v : Literal) /-- A sort. Universe levels are ignored. -/ - | sort : Key + | sort /-- A lambda function. -/ - | lam : Key + | lam /-- A dependent arrow. -/ - | forall : Key + | forall /-- A projection. It stores the structure name, the projection index and the arity. -/ - | proj : Name → Nat → Nat → Key - deriving Inhabited, BEq, Repr + | proj (typeName : Name) (idx nargs : Nat) + deriving Inhabited, BEq +/- +At the root, `.const` is the most common key, and it is very uncommon +to get the same contant name with a different arity. +So for performance, we just use `hash name` to hash `.const name _`. +-/ private nonrec def Key.hash : Key → UInt64 - | .star i => mixHash 7883 <| hash i - | .opaque => 342 - | .const n a => mixHash 5237 <| mixHash (hash n) (hash a) - | .fvar n a => mixHash 8765 <| mixHash (hash n) (hash a) - | .bvar i a => mixHash 4323 <| mixHash (hash i) (hash a) - | .lit v => mixHash 1879 <| hash v - | .sort => 2411 - | .lam => 4742 - | .«forall» => 9752 - | .proj s i a => mixHash (hash a) <| mixHash (hash s) (hash i) + | .star => 0 + | .labelledStar id => mixHash 5 <| hash id + | .opaque => 1 + | .const name _ => hash name + | .fvar fvarId nargs => mixHash 6 <| mixHash (hash fvarId) (hash nargs) + | .bvar idx nargs => mixHash 7 <| mixHash (hash idx) (hash nargs) + | .lit v => mixHash 8 <| hash v + | .sort => 2 + | .lam => 3 + | .«forall» => 4 + | .proj name idx nargs => mixHash (hash nargs) <| mixHash (hash name) (hash idx) instance : Hashable Key := ⟨Key.hash⟩ -/-- Constructor index used for ordering `Key`. -Note that the index of the star pattern is 0, so that when looking up in a `Trie`, -we can look at the start of the sorted array for all `.star` patterns. -/ -def Key.ctorIdx : Key → Nat - | .star .. => 0 - | .opaque .. => 1 - | .const .. => 2 - | .fvar .. => 3 - | .bvar .. => 4 - | .lit .. => 5 - | .sort => 6 - | .lam => 7 - | .forall => 8 - | .proj .. => 9 - -/-- The order on `Key` used in the `RefinedDiscrTree`. -/ -private def Key.lt : Key → Key → Bool - | .star i₁, .star i₂ => i₁ < i₂ - | .const n₁ a₁, .const n₂ a₂ => Name.quickLt n₁ n₂ || (n₁ == n₂ && a₁ < a₂) - | .fvar f₁ a₁, .fvar f₂ a₂ => Name.quickLt f₁.name f₂.name || (f₁ == f₂ && a₁ < a₂) - | .bvar i₁ a₁, .bvar i₂ a₂ => i₁ < i₂ || (i₁ == i₂ && a₁ < a₂) - | .lit v₁, .lit v₂ => v₁ < v₂ - | .proj s₁ i₁ a₁, .proj s₂ i₂ a₂ => Name.quickLt s₁ s₂ || - (s₁ == s₂ && (i₁ < i₂ || (i₁ == i₂ && a₁ < a₂))) - | k₁, k₂ => k₁.ctorIdx < k₂.ctorIdx - -instance : LT Key := ⟨fun a b => Key.lt a b⟩ -instance (a b : Key) : Decidable (a < b) := inferInstanceAs (Decidable (Key.lt a b)) - private def Key.format : Key → Format - | .star i => "*" ++ Std.format i + | .star => f!"*" + | .labelledStar id => f!"*{id}" | .opaque => "◾" - | .const k a => "⟨" ++ Std.format k ++ ", " ++ Std.format a ++ "⟩" - | .fvar k a => "⟨" ++ Std.format k.name ++ ", " ++ Std.format a ++ "⟩" - | .lit (Literal.natVal v) => Std.format v - | .lit (Literal.strVal v) => repr v - | .sort => "sort" - | .bvar i a => "⟨" ++ "#" ++ Std.format i ++ ", " ++ Std.format a ++ "⟩" + | .const name nargs => f!"⟨{name}, {nargs}⟩" + | .fvar fvarId nargs => f!"⟨{fvarId.name}, {nargs}⟩" + | .lit (Literal.natVal n) => f!"{n}" + | .lit (Literal.strVal s) => f!"{s.quote}" + | .sort => "Sort" + | .bvar i nargs => f!"⟨#{i}, {nargs}⟩" | .lam => "λ" | .forall => "∀" - | .proj s i a => "⟨" ++ Std.format s ++"."++ Std.format i ++", "++ Std.format a ++ "⟩" + | .proj name idx nargs => f!"⟨{name}.{idx}, {nargs}⟩" instance : ToFormat Key := ⟨Key.format⟩ +/-- +Converts an entry (i.e., `List Key`) to the discrimination tree into +`MessageData` that is more user-friendly. + +This is a copy of `Lean.Meta.DiscrTree.keysAsPattern` +-/ +partial def keysAsPattern (keys : Array Key) : CoreM MessageData := do + let (msg, keys) ← go (paren := false) |>.run keys.toList + if !keys.isEmpty then + throwError "illegal discrimination tree entry: {keys.map Key.format}" + return msg +where + /-- Get the next key. -/ + next : StateRefT (List Key) CoreM Key := do + let key :: keys ← get | throwError "illegal discrimination tree entry: {keys.map Key.format}" + set keys + return key + /-- Format the application `f args`. -/ + mkApp (f : MessageData) (nargs : Nat) (paren : Bool) : StateRefT (List Key) CoreM MessageData := + if nargs == 0 then + return f + else do + let mut r := m!"" + for _ in [:nargs] do + r := r ++ Format.line ++ (← go) + r := f ++ .nest 2 r + if paren then + return .paren r + else + return .group r + + /-- Format the next expression. -/ + go (paren := true) : StateRefT (List Key) CoreM MessageData := do + let key ← next + match key with + | .const declName nargs => + mkApp m!"{← mkConstWithLevelParams declName}" nargs paren + | .fvar fvarId nargs => + mkApp m!"{mkFVar fvarId}" nargs paren + | .proj _ i nargs => + mkApp m!"{← go}.{i+1}" nargs paren + | .bvar i nargs => + mkApp m!"#{i}" nargs paren + | .lam => + return parenthesize m!"λ, {← go (paren := false)}" paren + | .forall => + return parenthesize m!"{← go} → {← go (paren := false)}" paren + | _ => return key.format + /-- Add parentheses if `paren == true`. -/ + parenthesize (msg : MessageData) (paren : Bool) : MessageData := + if paren then msg.paren else msg.group + /-- Return the number of arguments that the `Key` takes. -/ def Key.arity : Key → Nat - | .const _ a => a - | .fvar _ a => a - | .bvar _ a => a - | .lam => 1 - | .forall => 2 - | .proj _ _ a => 1 + a - | _ => 0 + | .const _ nargs => nargs + | .fvar _ nargs => nargs + | .bvar _ nargs => nargs + | .lam => 1 + | .forall => 2 + | .proj _ _ nargs => nargs + 1 + | _ => 0 + +/-- The information for computing the keys of a subexpression. -/ +structure ExprInfo where + /-- The expression -/ + expr : Expr + /-- Variables that come from a lambda or forall binder. + The list index gives the De Bruijn index. -/ + bvars : List FVarId := [] + /-- The local context, which contains the introduced bound variables. -/ + lctx : LocalContext + /-- The local instances, which may contain the introduced bound variables. -/ + localInsts : LocalInstances + /-- The `Meta.Config` used by this entry. -/ + cfg : Config + /-- The current transparency level. Recall that unification uses the `default` + transparency level when unifying implicit arguments. So we index implicit arguments -/ + transparency : TransparencyMode + +/-- Creates an `ExprInfo` using the current context. -/ +def mkExprInfo (expr : Expr) (bvars : List FVarId) : MetaM ExprInfo := + return { + expr, bvars, + lctx := ← getLCtx + localInsts := ← getLocalInstances + cfg := ← getConfig + transparency := ← getTransparency + } + +/-- The possible values that can appear in the stack -/ +inductive StackEntry where + /-- `.star` is an expression that will not be explicitly indexed. -/ + | star + /-- `.expr` is an expression that will be indexed. -/ + | expr (info : ExprInfo) + +private def StackEntry.format : StackEntry → Format + | .star => f!".star" + | .expr info => f!".expr {info.expr}" + +instance : ToFormat StackEntry := ⟨StackEntry.format⟩ + +/-- A `LazyEntry` represents a snapshot of the computation of encoding an `Expr` as `Array Key`. +This is used for computing the keys one by one. -/ +structure LazyEntry where + /-- + If an expression creates more entries in the stack, for example because it is an application, + then instead of pushing to the stack greedily, we only extend the stack once we need to. + So, the field `previous` is used to extend the `stack` before looking in the `stack`. + + For example in `10.add (20.add 30)`, after computing the key `⟨Nat.add, 2⟩`, the stack is still + empty, and `previous` will be `10.add (20.add 30)`. + -/ + previous : Option ExprInfo := none + /-- + The stack, used to emulate recursion. It contains the list of all expressions for which the + keys still need to be computed, in that order. + + For example in `10.add (20.add 30)`, after computing the keys `⟨Nat.add, 2⟩` and `10`, the stack + will be a list of length 1 containing the expression `20.add 30`. + -/ + stack : List StackEntry := [] + /-- The metavariable context, which may contain variables appearing in this entry. -/ + mctx : MetavarContext + /-- + `MVarId`s corresponding to the `.labelledStar` labels. The index in the array is the label. + It is `none` if we use `.star` instead of `labelledStar`, + for example when encoding the lookup expression. + -/ + labelledStars? : Option (Array MVarId) + /-- + The `Key`s that have already been computed. + + Sometimes, more than one `Key` ends up being computed in one go. This happens when + there are lambda binders (because it depends on the body whether the lambda key + should be indexed or not). In that case the remaining `Key`s are stored in `results`. + -/ + computedKeys : List Key := [] +deriving Inhabited + +/-- Creates a `LazyEntry` using the current metavariable context. -/ +def mkInitLazyEntry (labelledStars : Bool) : MetaM LazyEntry := + return { + mctx := ← getMCtx + labelledStars? := if labelledStars then some #[] else none + } + +private def LazyEntry.format (entry : LazyEntry) : Format := Id.run do + let mut parts := #[f!"stack: {entry.stack}"] + unless entry.computedKeys == [] do + parts := parts.push f!"results: {entry.computedKeys}" + if let some info := entry.previous then + parts := parts.push f!"todo: {info.expr}" + return Format.joinSep parts.toList ", " + +instance : ToFormat LazyEntry := ⟨LazyEntry.format⟩ + +/-- Array index of a `Trie α` in the `tries` of a `RefinedDiscrTree`. -/ +abbrev TrieIndex := Nat + +/-- +Discrimination tree trie. See `RefinedDiscrTree`. + +A `Trie` will normally have exactly one of the following +- nonempty `values` +- nonempty `stars`, `labelledStars` and/or `children` +- nonempty `pending` +But defining it as a structure that can have all at the same time turns out to be easier. +-/ +structure Trie (α : Type) where + node :: + /-- Return values, at a leaf -/ + values : Array α + /-- Following `Trie`s based on a `Key.star`. -/ + star : Option TrieIndex + /-- Following `Trie`s based on a `Key.labelledStar`. -/ + labelledStars : Std.HashMap Nat TrieIndex + /-- Following `Trie`s based on the `Key`. -/ + children : Std.HashMap Key TrieIndex + /-- Lazy entries that still have to be evaluated. -/ + pending : Array (LazyEntry × α) + +instance {α : Type} : Inhabited (Trie α) := ⟨.node #[] none {} {} #[]⟩ + +end RefinedDiscrTree + +open RefinedDiscrTree in + +/-- +Lazy refined discrimination tree. It is an index from expressions to values of type `α`. + +We store all of the nodes in one `Array`, `tries`, instead of using a 'normal' inductive type. +This is so that we can modify the tree globally, which is very useful when evaluating lazy +entries and saving the result globally. +-/ +structure RefinedDiscrTree (α : Type) where + /-- `Trie`s at the root based of the `Key`. -/ + root : Std.HashMap Key TrieIndex := {} + /-- Array of trie entries. Should be owned by this trie. -/ + tries : Array (Trie α) := #[] +deriving Inhabited + +namespace RefinedDiscrTree variable {α : Type} -/-- Discrimination tree trie. See `RefinedDiscrTree`. -/ -inductive Trie (α : Type) where - /-- Map from `Key` to `Trie`. Children is an `Array` of size at least 2, - sorted in increasing order using `Key.lt`. -/ - | node (children : Array (Key × Trie α)) - /-- Sequence of nodes with only one child. `keys` is an `Array` of size at least 1. -/ - | path (keys : Array Key) (child : Trie α) - /-- Leaf of the Trie. `values` is an `Array` of size at least 1. -/ - | values (vs : Array α) -instance : Inhabited (Trie α) := ⟨.node #[]⟩ - -/-- `Trie.path` constructor that only inserts the path if it is non-empty. -/ -def Trie.mkPath (keys : Array Key) (child : Trie α) := - if keys.isEmpty then child else Trie.path keys child - -/-- `Trie` constructor for a single value, taking the keys starting at index `i`. -/ -def Trie.singleton (keys : Array Key) (value : α) (i : Nat) : Trie α := - mkPath keys[i:] (values #[value]) - -/-- `Trie.node` constructor for combining two `Key`, `Trie α` pairs. -/ -def Trie.mkNode2 (k1 : Key) (t1 : Trie α) (k2 : Key) (t2 : Trie α) : Trie α := - if k1 < k2 then - .node #[(k1, t1), (k2, t2)] + +private partial def format [ToFormat α] (tree : RefinedDiscrTree α) : Format := + let lines := tree.root.fold (init := #[]) fun lines key trie => + lines.push (Format.nest 2 f!"{key} =>{Format.line}{go trie}") + if lines.size = 0 then + f!"" else - .node #[(k2, t2), (k1, t1)] - -/-- Return the values from a `Trie α`, assuming that it is a leaf -/ -def Trie.values! : Trie α → Array α - | .values vs => vs - | _ => panic! "expected .values constructor" - -/-- Return the children of a `Trie α`, assuming that it is not a leaf. -The result is sorted by the `Key`'s -/ -def Trie.children! : Trie α → Array (Key × Trie α) - | .node cs => cs - | .path ks c => #[(ks[0]!, mkPath ks[1:] c)] - | .values _ => panic! "did not expect .values constructor" - -private partial def Trie.format [ToFormat α] : Trie α → Format - | .node cs => Format.group <| Format.paren <| - "node " ++ Format.join (cs.toList.map fun (k, c) => - Format.line ++ Format.paren (format (prepend k c))) - | .values vs => if vs.isEmpty then Format.nil else Std.format vs - | .path ks c => Format.sbracket (Format.joinSep ks.toList (", ")) - ++ " => " ++ Format.line ++ format c + "Discrimination tree flowchart:" ++ Format.joinSep lines.toList "\n" where - prepend (k : Key) (t : Trie α) : Trie α := match t with - | .path ks c => .path (#[k] ++ ks) c - | t => .path #[k] t -instance [ToFormat α] : ToFormat (Trie α) := ⟨Trie.format⟩ - - -/-- Discrimination tree. It is an index from expressions to values of type `α`. -/ -structure _root_.Lean.Meta.RefinedDiscrTree (α : Type) where - /-- The underlying `PersistentHashMap` of a `RefinedDiscrTree`. -/ - root : PersistentHashMap Key (Trie α) := {} -instance : Inhabited (RefinedDiscrTree α) := ⟨{}⟩ - -private partial def format [ToFormat α] (d : RefinedDiscrTree α) : Format := - let (_, r) := d.root.foldl - (fun (p : Bool × Format) k c => - (false, - p.2 ++ (if p.1 then Format.nil else Format.line) ++ - Format.paren (Std.format k ++ " => " ++ Std.format c))) - (true, Format.nil) - Format.group r + go (trie : TrieIndex) : Format := Id.run do + let { values, star, labelledStars, children, pending } := tree.tries[trie]! + let mut lines := #[] + unless pending.isEmpty do + lines := lines.push f!"pending entries: {pending.map (·.2)}" + unless values.isEmpty do + lines := lines.push f!"entries: {values}" + if let some trie := star then + lines := lines.push (Format.nest 2 f!"* =>{Format.line}{go trie}") + lines := labelledStars.fold (init := lines) fun lines key trie => + lines.push (Format.nest 2 f!"*{key} =>{Format.line}{go trie}") + lines := children.fold (init := lines) fun lines key trie => + lines.push (Format.nest 2 f!"{key} =>{Format.line}{go trie}") + if lines.isEmpty then + f!"" + else + Format.joinSep lines.toList "\n" instance [ToFormat α] : ToFormat (RefinedDiscrTree α) := ⟨format⟩ diff --git a/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean b/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean index a5c49e2a3d4085..0431252c3007e0 100644 --- a/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean +++ b/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean @@ -1,508 +1,321 @@ /- -Copyright (c) 2023 Jovan Gerbscheid. All rights reserved. +Copyright (c) 2024 Jovan Gerbscheid. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Jovan Gerbscheid -/ import Mathlib.Lean.Meta.RefinedDiscrTree.Basic -import Mathlib.Lean.Meta.RefinedDiscrTree.Pi -import Mathlib.Tactic.FunProp.StateList +import Lean.Meta.DiscrTree /-! # Encoding an `Expr` as a sequence of `Key`s --/ - -open Mathlib.Meta.FunProp (StateListT StateListM) - -namespace Lean.Meta.RefinedDiscrTree -variable {α} - -/-- `DTExpr` is a simplified form of `Expr`. -It is the intermediate step for converting from `Expr` to `Array Key`. -/ -inductive DTExpr where - /-- A metavariable. It optionally stores an `MVarId`. -/ - | star : Option MVarId → DTExpr - /-- An opaque variable or a let-expression in the case `WhnfCoreConfig.zeta := false`. -/ - | opaque : DTExpr - /-- A constant. It stores the name and the arguments. -/ - | const : Name → Array DTExpr → DTExpr - /-- A free variable. It stores the `FVarId` and the arguments -/ - | fvar : FVarId → Array DTExpr → DTExpr - /-- A bound variable. It stores the De Bruijn index and the arguments -/ - | bvar : Nat → Array DTExpr → DTExpr - /-- A literal. -/ - | lit : Literal → DTExpr - /-- A sort. -/ - | sort : DTExpr - /-- A lambda function. It stores the body. -/ - | lam : DTExpr → DTExpr - /-- A dependent arrow. It stores the domain and body. -/ - | forall : DTExpr → DTExpr → DTExpr - /-- A projection. It stores the structure name, projection index, struct body and arguments. -/ - | proj : Name → Nat → DTExpr → Array DTExpr → DTExpr -deriving Inhabited, BEq, Repr - -private partial def DTExpr.format : DTExpr → Format - | .star _ => "*" - | .opaque => "◾" - | .const n as => Std.format n ++ formatArgs as - | .fvar n as => Std.format n.name ++ formatArgs as - | .bvar i as => "#" ++ Std.format i ++ formatArgs as - | .lit (Literal.natVal v) => Std.format v - | .lit (Literal.strVal v) => repr v - | .sort => "Sort" - | .lam b => "λ " ++ DTExpr.format b - | .forall d b => DTExpr.format d ++ " → " ++ DTExpr.format b - | .proj _ i a as => DTExpr.format a ++ "." ++ Std.format i ++ formatArgs as -where - formatArgs (as : Array DTExpr) := - if as.isEmpty - then .nil - else " " ++ Format.paren (@Format.joinSep _ ⟨DTExpr.format⟩ as.toList ", ") - -instance : ToFormat DTExpr := ⟨DTExpr.format⟩ - -/-- Return the size of the `DTExpr`. This is used for calculating the matching score when two -expressions are equal. -The score is not incremented at a lambda, which is so that the expressions -`∀ x, p[x]` and `∃ x, p[x]` get the same size. -/ -partial def DTExpr.size : DTExpr → Nat -| .const _ args -| .fvar _ args -| .bvar _ args => args.foldl (init := 1) (· + ·.size) -| .lam b => b.size -| .forall d b => 1 + d.size + b.size -| _ => 1 - -/-- Determine if two `DTExpr`s are equivalent. -/ -def DTExpr.eqv (a b : DTExpr) : Bool := - (go a b).run' {} -where - @[nolint docBlame] - go (a b : DTExpr) : StateM (Std.HashMap MVarId MVarId) Bool := - match a, b with - | .opaque , .opaque => pure true - | .const n₁ as₁ , .const n₂ as₂ => pure (n₁ == n₂) <&&> goArray as₁ as₂ - | .fvar n₁ as₁ , .fvar n₂ as₂ => pure (n₁ == n₂) <&&> goArray as₁ as₂ - | .bvar i₁ as₁ , .bvar i₂ as₂ => pure (i₁ == i₂) <&&> goArray as₁ as₂ - | .lit li₁ , .lit li₂ => pure (li₁ == li₂) - | .sort , .sort => pure true - | .lam b₁ , .lam b₂ => go b₁ b₂ - | .forall d₁ b₁ , .forall d₂ b₂ => go d₁ d₂ <&&> go b₁ b₂ - | .proj n₁ i₁ a₁ as₁, .proj n₂ i₂ a₂ as₂ => pure (n₁ == n₂ && i₁ == i₂) - <&&> go a₁ a₂ <&&> goArray as₁ as₂ - | .star none , .star none => pure true - | .star (some id₁) , .star (some id₂) => modifyGet fun map => match map[id₁]? with - | some id => (id == id₂, map) - | none => (true, map.insert id₁ id₂) - | _ , _ => return false - - @[nolint docBlame] - goArray (as bs : Array DTExpr) : StateM (Std.HashMap MVarId MVarId) Bool := do - if h : as.size = bs.size then - for g : i in [:as.size] do - unless ← go as[i] (bs[i]'(h ▸ g.2.1)) do - return false - return true - else - return false - -/-! ## Encoding an Expr -/ - -/-- This state is used to turn the indexing by `MVarId` and `FVarId` in `DTExpr` into -indexing by `Nat` in `Key`. -/ -private structure Flatten.State where - stars : Array MVarId := #[] - -private def getStar (mvarId? : Option MVarId) : StateM Flatten.State Nat := - modifyGet fun s => - match mvarId? with - | some mvarId => match s.stars.findIdx? (· == mvarId) with - | some idx => (idx, s) - | none => (s.stars.size, { s with stars := s.stars.push mvarId }) - | none => (s.stars.size, { s with stars := s.stars.push ⟨.anonymous⟩ }) - -private partial def DTExpr.flattenAux (todo : Array Key) : DTExpr → StateM Flatten.State (Array Key) - | .star i => return todo.push (.star (← getStar i)) - | .opaque => return todo.push .opaque - | .const n as => as.foldlM flattenAux (todo.push (.const n as.size)) - | .fvar f as => as.foldlM flattenAux (todo.push (.fvar f as.size)) - | .bvar i as => as.foldlM flattenAux (todo.push (.bvar i as.size)) - | .lit l => return todo.push (.lit l) - | .sort => return todo.push .sort - | .lam b => flattenAux (todo.push .lam) b - | .«forall» d b => do flattenAux (← flattenAux (todo.push .forall) d) b - | .proj n i e as => do as.foldlM flattenAux (← flattenAux (todo.push (.proj n i as.size)) e) - -/-- Given a `DTExpr`, return the linearized encoding in terms of `Key`, -which is used for `RefinedDiscrTree` indexing. -/ -def DTExpr.flatten (e : DTExpr) (initCapacity := 16) : Array Key := - (DTExpr.flattenAux (.mkEmpty initCapacity) e).run' {} - - - -/-- Return true if `e` is one of the following -- A nat literal (numeral) -- `Nat.zero` -- `Nat.succ x` where `isNumeral x` -- `OfNat.ofNat _ x _` where `isNumeral x` -/ -private partial def isNumeral (e : Expr) : Bool := - if e.isRawNatLit then true - else - let f := e.getAppFn - if !f.isConst then false - else - let fName := f.constName! - if fName == ``Nat.succ && e.getAppNumArgs == 1 then isNumeral e.appArg! - else if fName == ``OfNat.ofNat && e.getAppNumArgs == 3 then isNumeral (e.getArg! 1) - else if fName == ``Nat.zero && e.getAppNumArgs == 0 then true - else false - -/-- Return `some n` if `e` is definitionally equal to the natural number `n`. -/ -private partial def toNatLit? (e : Expr) : Option Literal := - if isNumeral e then - if let some n := loop e then - some (.natVal n) - else - none - else - none -where - loop (e : Expr) : Option Nat := do - let f := e.getAppFn - match f with - | .lit (.natVal n) => return n - | .const fName .. => - if fName == ``Nat.succ && e.getAppNumArgs == 1 then - let r ← loop e.appArg! - return r+1 - else if fName == ``OfNat.ofNat && e.getAppNumArgs == 3 then - loop (e.getArg! 1) - else if fName == ``Nat.zero && e.getAppNumArgs == 0 then - return 0 - else - failure - | _ => failure - -/-- Reduction procedure for the `RefinedDiscrTree` indexing. -/ -partial def reduce (e : Expr) : MetaM Expr := do - let e ← whnfCore e - match (← unfoldDefinition? e) with - | some e => reduce e - | none => match e.etaExpandedStrict? with - | some e => reduce e - | none => return e - -/-- Repeatedly apply reduce while stripping lambda binders and introducing their variables -/ -@[specialize] -partial def lambdaTelescopeReduce {m} [Monad m] [MonadLiftT MetaM m] [MonadControlT MetaM m] - [Nonempty α] (e : Expr) (fvars : List FVarId) - (k : Expr → List FVarId → m α) : m α := do - match ← reduce e with - | .lam n d b bi => - withLocalDecl n bi d fun fvar => - lambdaTelescopeReduce (b.instantiate1 fvar) (fvar.fvarId! :: fvars) k - | e => k e fvars - - -/-- Check whether the expression is represented by `Key.star`. -/ -def isStar : Expr → Bool - | .mvar .. => true - | .app f _ => isStar f - | _ => false +We compute the encoding of an expression in a lazy way. +This means computing only one `Key` at a time +and storing the state of the remaining computation in a `LazyEntry`. -/-- Check whether the expression is represented by `Key.star` and has `arg` as an argument. -/ -def isStarWithArg (arg : Expr) : Expr → Bool - | .app f a => if a == arg then isStar f else isStarWithArg arg f - | _ => false +Each step is computed by +`evalLazyEntryWithEta : LazyEntry → MetaM (Option (List (Key × LazyEntry)))`. +It returns `none` when the last `Key` has already been reached. -private partial def DTExpr.hasLooseBVarsAux (i : Nat) : DTExpr → Bool - | .const _ as => as.any (hasLooseBVarsAux i) - | .fvar _ as => as.any (hasLooseBVarsAux i) - | .bvar j as => j ≥ i || as.any (hasLooseBVarsAux i) - | .proj _ _ a as => a.hasLooseBVarsAux i || as.any (hasLooseBVarsAux i) - | .forall d b => d.hasLooseBVarsAux i || b.hasLooseBVarsAux (i+1) - | .lam b => b.hasLooseBVarsAux (i+1) - | _ => false +The first step, which is used when initializing the tree, +is computed by `initializeLazyEntryWithEta`. -/-- Return `true` if `e` contains a loose bound variable. -/ -def DTExpr.hasLooseBVars (e : DTExpr) : Bool := - e.hasLooseBVarsAux 0 +To compute all the keys at once, we have +* `encodeExprWithEta`, which computes all possible key sequences. +* `encodeExpr`, which computes the canonical key sequence. + This will be used for expressions that are looked up in a `RefinedDiscrTree` using `getMatch`. +-/ -namespace MkDTExpr +namespace Lean.Meta.RefinedDiscrTree +/-- The context for the `LazyM` monad -/ private structure Context where /-- Variables that come from a lambda or forall binder. The list index gives the De Bruijn index. -/ - bvars : List FVarId := [] - /-- Variables that come from a lambda that has been removed via η-reduction. -/ - forbiddenVars : List FVarId := [] - fvarInContext : FVarId → Bool + bvars : List FVarId -/-- Return for each argument whether it should be ignored. -/ -def getIgnores (fn : Expr) (args : Array Expr) : MetaM (Array Bool) := do - let mut fnType ← inferType fn - let mut result := Array.mkEmpty args.size - let mut j := 0 - for h : i in [:args.size] do - unless fnType matches .forallE .. do - fnType ← whnfD (fnType.instantiateRevRange j i args) - j := i - let .forallE _ d b bi := fnType | throwError m! "expected function type {indentExpr fnType}" - fnType := b - result := result.push (← isIgnoredArg args[i] d bi) - return result -where - /-- Return whether the argument should be ignored. -/ - isIgnoredArg (arg domain : Expr) (binderInfo : BinderInfo) : MetaM Bool := do - if domain.isOutParam then - return true - match binderInfo with - | .instImplicit => return true - | .implicit - | .strictImplicit => return !(← isType arg) - | .default => isProof arg +/-- The monad used for evaluating a `LazyEntry`. -/ +private abbrev LazyM := ReaderT Context <| StateT LazyEntry MetaM +private def mkLabelledStar (mvarId : MVarId) : LazyM Key := + modifyGet fun entry => + if let some stars := entry.labelledStars? then + match stars.idxOf? mvarId with + | some idx => (.labelledStar idx, entry) + | none => (.labelledStar stars.size, { entry with labelledStars? := stars.push mvarId }) + else + (.star, entry) -@[specialize] -private def withLams {m} [Monad m] [MonadWithReader Context m] - (lambdas : List FVarId) (k : m DTExpr) : m DTExpr := - if lambdas.isEmpty then - k - else do - let e ← withReader (fun c => { c with bvars := lambdas ++ c.bvars }) k - return lambdas.foldl (fun _ => ·.lam) e - - -/-- Return the encoding of `e` as a `DTExpr`. -If `root = false`, then `e` is a strict sub expression of the original expression. -/ -partial def mkDTExprAux (e : Expr) (root : Bool) : ReaderT Context MetaM DTExpr := do - lambdaTelescopeReduce e [] fun e lambdas => - e.withApp fun fn args => do - - let argDTExpr (arg : Expr) (ignore : Bool) : ReaderT Context MetaM DTExpr := - if ignore then pure (.star none) else mkDTExprAux arg false - - let argDTExprs : ReaderT Context MetaM (Array DTExpr) := do - let ignores ← getIgnores fn args - args.mapIdxM fun i arg => - argDTExpr arg ignores[i]! - - match fn with +/-- +Sometimes, we need to not index lambda binders, in particular when the body is the application of +a metavariable. + +In the case where we do index the lambda binders, +`withLams` efficiently adds the lambdas and `key` to the result. +-/ +@[inline] +private def withLams (lambdas : List FVarId) (key : Key) : StateT LazyEntry MetaM Key := do + match lambdas with + | [] => return key + | _ :: tail => + -- Add `key` and `lambdas.length - 1` lambdas to the result, returning the final lambda. + modify ({ · with computedKeys := tail.foldl (init := [key]) (fun _ => .lam :: ·) }) + return .lam + +open private Lean.Meta.DiscrTree.pushArgs in Lean.Meta.DiscrTree.mkPathAux in +open private toNatLit? in Lean.Meta.DiscrTree.pushArgs in + +@[inline] +private def encodingStepAux (e : Expr) (lambdas : List FVarId) (root : Bool) : LazyM Key := do + withLams lambdas (← go) +where + go := do + /- + If entries need to be added to the stack, we don't do that now, because of the lazy design. + Instead, we set `previous` to be `e`, and later, + `processPrevious` adds the required entries to the stack. + -/ + let setEAsPrevious : LazyM Unit := do + let info ← mkExprInfo e (lambdas ++ (← read).bvars) + modify fun s => { s with previous := some info } + + match e.getAppFn with | .const n _ => unless root do - if let some (type, lhs, rhs, lambdas') ← reduceHBinOp n args lambdas then - return ← withLams lambdas' do - let type ← mkDTExprAux type false - let lhs ← mkDTExprAux lhs false - let rhs ← mkDTExprAux rhs false - return .const n #[type, type, .star none, .star none, lhs, rhs] - - if let some (type, arg, lambdas') ← reduceUnOp n e.getAppArgs lambdas then - return ← withLams lambdas' do - let type ← mkDTExprAux type false - let arg ← mkDTExprAux arg false - return .const n #[type, .star none, arg] - - /- since `(fun _ => 0) = 0` and `(fun _ => 1) = 1`, - we don't index lambdas before literals -/ if let some v := toNatLit? e then return .lit v - withLams lambdas do - return .const n (← argDTExprs) - | .proj s i a => - withLams lambdas do - let a ← argDTExpr a (isClass (← getEnv) s) - return .proj s i a (← argDTExprs) + if e.getAppNumArgs != 0 then + setEAsPrevious + return .const n e.getAppNumArgs + | .proj n i _ => + setEAsPrevious + return .proj n i e.getAppNumArgs | .fvar fvarId => - /- we index `fun x => x` as `id` when not at the root -/ - if let fvarId' :: lambdas' := lambdas then - if fvarId' == fvarId && args.isEmpty && !root then - return ← withLams lambdas' do - let type ← mkDTExprAux (← fvarId.getType) false - return .const ``id #[type] - withLams lambdas do - if let some idx := (← read).bvars.findIdx? (· == fvarId) then - return .bvar idx (← argDTExprs) - if (← read).fvarInContext fvarId then - return .fvar fvarId (← argDTExprs) - else - return .opaque + let bvars := lambdas ++ (← read).bvars + if e.getAppNumArgs != 0 then + setEAsPrevious + if let some idx := bvars.idxOf? fvarId then + return .bvar idx e.getAppNumArgs + else + return .fvar fvarId e.getAppNumArgs | .mvar mvarId => - /- When the mvarId has arguments, index it with `[*]` instead of `[λ,*]`, - because the star could depend on the bound variables. As a result, - something indexed `[λ,*]` has that the `*` cannot depend on the λ-bound variables -/ - if args.isEmpty then - withLams lambdas do return .star (some mvarId) + if e.isApp then + /- + If `e.isApp`, we don't index `lambdas`, + since for example `fun x => ?m x x` may be any function. + -/ + return .star else - return .star none - - | .forallE n d b bi => - withLams lambdas do - let d' ← mkDTExprAux d false - let b' ← withLocalDecl n bi d fun fvar => - withReader (fun c => { c with bvars := fvar.fvarId! :: c.bvars }) do - mkDTExprAux (b.instantiate1 fvar) false - return .forall d' b' - | .lit v => withLams lambdas do return .lit v - | .sort _ => withLams lambdas do return .sort - | .letE .. => withLams lambdas do return .opaque - | .lam .. => withLams lambdas do return .opaque + /- + If `e` is `.mvar mvarId`, we do index `lambdas`, since it is a constant function. + We create a `.labelledStar` key that is identified by `mvarId`, + so that multiple appearances of `.mvar mvarId` are indexed the same. + -/ + mkLabelledStar mvarId + | .forallE .. => + setEAsPrevious + return .forall + | .lit v => return .lit v + | .sort _ => return .sort + | .letE .. => return .opaque + | .lam .. => return .opaque | _ => unreachable! -private abbrev M := StateListT (AssocList Expr DTExpr) <| ReaderT Context MetaM - -/- -Caching values is a bit dangerous, because when two expressions are be equal and they live under -a different number of binders, then the resulting De Bruijn indices are offset. -In practice, getting a `.bvar` in a `DTExpr` is very rare, so we exclude such values from the cache. --/ -instance : MonadCache Expr DTExpr M where - findCached? e := do - let s ← get - return s.find? e - cache e e' := - if e'.hasLooseBVars then - return - else - modify (·.insert e e') +/-- Run `k` on all pairs of body, bound variables that could possibly appear due to η-reduction -/ +private def etaPossibilities (e : Expr) (lambdas : List FVarId) (root : Bool) + (entry : LazyEntry) : ReaderT Context MetaM (List (Key × LazyEntry)) := do + return (← (encodingStepAux e lambdas root).run (← read) |>.run entry) :: + (← match e, lambdas with + | .app f a, fvarId :: lambdas => + if isStarWithArg (.fvar fvarId) a && !f.getAppFn.isMVar then + etaPossibilities f lambdas root entry + else + return [] + | _, _ => return []) +where + /-- Check whether the expression is represented by `Key.star` and has `arg` as an argument. -/ + isStarWithArg (arg : Expr) : Expr → Bool + | .app f a => if a == arg then f.getAppFn.isMVar else isStarWithArg arg f + | _ => false -/-- Return all pairs of body, bound variables that could possibly appear due to η-reduction -/ +/-- Repeatedly reduce while stripping lambda binders and introducing their variables -/ @[specialize] -def etaPossibilities (e : Expr) (lambdas : List FVarId) (k : Expr → List FVarId → M α) : M α := - k e lambdas - <|> do - match e, lambdas with - | .app f a, fvarId :: lambdas => - if isStarWithArg (.fvar fvarId) a then - withReader (fun c => { c with forbiddenVars := fvarId :: c.forbiddenVars }) do - etaPossibilities f lambdas k +private partial def lambdaTelescopeReduce {m} {α} [Nonempty (m α)] [Monad m] [MonadLiftT MetaM m] + [MonadControlT MetaM m] (e : Expr) (lambdas : List FVarId) (noIndex : List FVarId → m α) + (k : Expr → List FVarId → m α) : m α := do + /- expressions marked with `no_index` should be indexed with a star -/ + if DiscrTree.hasNoindexAnnotation e then + noIndex lambdas + else + match ← DiscrTree.reduce e with + | .lam n d b bi => + withLocalDecl n bi d fun fvar => + lambdaTelescopeReduce (b.instantiate1 fvar) (fvar.fvarId! :: lambdas) noIndex k + | e => k e lambdas + +/-- A single step in encoding an `Expr` into `Key`s. -/ +private def encodingStepWithEta (e : Expr) (root : Bool) + (entry : LazyEntry) : ReaderT Context MetaM (List (Key × LazyEntry)) := + lambdaTelescopeReduce e [] + (fun lambdas => return [← (withLams lambdas .star).run entry]) + (fun e lambdas => etaPossibilities e lambdas root entry) + +/-- A single step in encoding an `Expr` into `Key`s. -/ +private def encodingStep (e : Expr) (root : Bool) : LazyM Key := do + lambdaTelescopeReduce e [] + (fun lambdas => withLams lambdas .star) + (fun e lambdas => encodingStepAux e lambdas root) + +/-- Encode `e` as a sequence of keys, computing only the first `Key`. -/ +@[inline] def initializeLazyEntryWithEtaAux (e : Expr) (labelledStars : Bool) : + MetaM (List (Key × LazyEntry)) := do + (encodingStepWithEta e true (← mkInitLazyEntry labelledStars)).run { bvars := [] } + + +/-- Encode `e` as a sequence of keys, computing only the first `Key`. -/ +def initializeLazyEntryWithEta (e : Expr) (labelledStars : Bool := true) : + MetaM (List (Key × LazyEntry)) := do + withReducible do initializeLazyEntryWithEtaAux e labelledStars + +/-- Encode `e` as a sequence of keys, computing only the first `Key`. -/ +private def initializeLazyEntry (e : Expr) (labelledStars : Bool) : MetaM (Key × LazyEntry) := do + ((encodingStep e true).run { bvars := [] }).run (← mkInitLazyEntry labelledStars) + + +/-- Auxiliary function for `evalLazyEntry` -/ +private partial def evalLazyEntryAux (entry : LazyEntry) (eta : Bool) : + MetaM (Option (List (Key × LazyEntry))) := do + match entry.stack with + | [] => return none + | stackEntry :: stack => + let entry := { entry with stack } + match stackEntry with + | .star => + return some [(.star, entry)] + | .expr { expr, bvars, lctx, localInsts, cfg, transparency } => + withLCtx lctx localInsts do + withConfig (fun _ => cfg) do withTransparency transparency do + if eta then + return some (← encodingStepWithEta expr false entry |>.run { bvars := bvars }) + else + return some [← encodingStep expr false |>.run { bvars := bvars } |>.run entry] + +/-- Determine for each argument whether it should be ignored, +and return a list consisting of one `StackEntry` for each argument. -/ +private partial def getStackEntries (fn : Expr) (args : Array Expr) (bvars : List FVarId) : + MetaM (List StackEntry) := do + let mut fnType ← inferType fn + loop fnType 0 0 [] +where + /-- The main loop of `getStackEntries` -/ + loop (fnType : Expr) (i j : Nat) (entries : List StackEntry) : MetaM (List StackEntry) := do + if h : i < args.size then + let arg := args[i] + let cont j d b bi := do + if ← isIgnoredArg arg d bi then + loop b (i+1) j (.star :: entries) + else + -- Recall that `isDefEq` switches the transparency on implicit arguments. + let info ← (if bi.isExplicit then id else withInferTypeConfig) do mkExprInfo arg bvars + loop b (i+1) j (.expr info :: entries) + let rec reduce := do + match ← whnfD (fnType.instantiateRevRange j i args) with + | .forallE _ d b bi => cont i d b bi + | fnType => throwFunctionExpected fnType + match fnType with + | .forallE _ d b bi => cont j d b bi + | _ => reduce else - failure - | _, _ => failure + return entries + /-- Determine whether the argument should be ignored. -/ + isIgnoredArg (arg domain : Expr) (binderInfo : BinderInfo) : MetaM Bool := do + if domain.isOutParam then + return true + else match binderInfo with + | .instImplicit => return true + | .implicit + | .strictImplicit => return !(← isType arg) + | .default => isProof arg -/-- run `etaPossibilities`, and cache the result if there are multiple possibilities. -/ -@[specialize] -def cacheEtaPossibilities (e original : Expr) (lambdas : List FVarId) - (k : Expr → List FVarId → M DTExpr) : M DTExpr := - match e, lambdas with - | .app _ a, fvarId :: _ => - if isStarWithArg (.fvar fvarId) a then - checkCache original fun _ => - etaPossibilities e lambdas k - else - k e lambdas - | _, _ => k e lambdas - - -/-- Return all encodings of `e` as a `DTExpr`, taking possible η-reductions into account. -If `root = false`, then `e` is a strict sub expression of the original expression. -/ -partial def mkDTExprsAux (original : Expr) (root : Bool) : M DTExpr := do - lambdaTelescopeReduce original [] fun e lambdas => do - - if !root then - if let .const n _ := e.getAppFn then - if let some (type, lhs, rhs, lambdas') ← reduceHBinOp n e.getAppArgs lambdas then - return ← withLams lambdas' do - let type ← mkDTExprsAux type false - let lhs ← mkDTExprsAux lhs false - let rhs ← mkDTExprsAux rhs false - return .const n #[type, type, .star none, .star none, lhs, rhs] - - if let some (type, arg, lambdas') ← reduceUnOp n e.getAppArgs lambdas then - return ← withLams lambdas' do - let type ← mkDTExprsAux type false - let arg ← mkDTExprsAux arg false - return .const n #[type, .star none, arg] - - cacheEtaPossibilities e original lambdas fun e lambdas => - e.withApp fun fn args => do - - let argDTExpr (arg : Expr) (ignore : Bool) : M DTExpr := - if ignore then pure (.star none) else mkDTExprsAux arg false - - let argDTExprs : M (Array DTExpr) := do - let ignores ← getIgnores fn args - args.mapIdxM fun i arg => - argDTExpr arg ignores[i]! - - match fn with - | .const n _ => - unless root do - /- since `(fun _ => 0) = 0` and `(fun _ => 1) = 1`, - we don't index lambdas before nat literals -/ - if let some v := toNatLit? e then - return .lit v - withLams lambdas do - return .const n (← argDTExprs) - | .proj s i a => - withLams lambdas do - let a ← argDTExpr a (isClass (← getEnv) s) - return .proj s i a (← argDTExprs) - | .fvar fvarId => - /- we index `fun x => x` as `id` when not at the root -/ - if let fvarId' :: lambdas' := lambdas then - if fvarId' == fvarId && args.isEmpty && !root then - return ← withLams lambdas' do - let type ← mkDTExprAux (← fvarId.getType) false - return .const ``id #[type] - withLams lambdas do - let c ← read - if let some idx := c.bvars.findIdx? (· == fvarId) then - return .bvar idx (← argDTExprs) - guard !(c.forbiddenVars.contains fvarId) - if c.fvarInContext fvarId then - return .fvar fvarId (← argDTExprs) +/-- +If `entry.previous.isSome`, then replace it with `none`, and add the required entries +to entry.stack`. +-/ +private def processPrevious (entry : LazyEntry) : MetaM LazyEntry := do + let some { expr, bvars, lctx, localInsts, cfg, transparency } := entry.previous | return entry + let entry := { entry with previous := none } + withLCtx lctx localInsts do withConfig (fun _ => cfg) do withTransparency transparency do + expr.withApp fun fn args => do + + let stackArgs (entry : LazyEntry) : MetaM LazyEntry := do + let entries ← getStackEntries fn args bvars + return { entry with stack := entries.reverseAux entry.stack } + + match fn with + | .forallE n d b bi => + let d' := .expr (← mkExprInfo d bvars) + let b' ← withLocalDecl n bi d fun fvar => + return .expr (← mkExprInfo (b.instantiate1 fvar) (fvar.fvarId! :: bvars)) + return { entry with stack := d' :: b' :: entry.stack } + | .proj n _ a => + let entry ← stackArgs entry + if isClass (← getEnv) n then + return { entry with stack := .star :: entry.stack } else - return .opaque - | .mvar mvarId => - /- When the mvarId has arguments, index it with `[*]` instead of `[λ,*]`, - because the star could depend on the bound variables. As a result, - something indexed `[λ,*]` has that the `*` cannot depend on the λ-bound variables -/ - if args.isEmpty then - withLams lambdas do return .star (some mvarId) - else - return .star none - - | .forallE n d b bi => - withLams lambdas do - let d' ← mkDTExprsAux d false - let b' ← withLocalDecl n bi d fun fvar => - withReader (fun c => { c with bvars := fvar.fvarId! :: c.bvars }) do - mkDTExprsAux (b.instantiate1 fvar) false - return .forall d' b' - | .lit v => withLams lambdas do return .lit v - | .sort _ => withLams lambdas do return .sort - | .letE .. => withLams lambdas do return .opaque - | .lam .. => withLams lambdas do return .opaque - | _ => unreachable! - -end MkDTExpr - -/-- Returns true if the `DTExpr` is not of the form `*` or `Eq * * *`". -/ -def DTExpr.isSpecific : DTExpr → Bool - | .star _ - | .const ``Eq #[.star _, .star _, .star _] => false - | _ => true - -/-- Return the encoding of `e` as a `DTExpr`. - -Warning: to account for potential η-reductions of `e`, use `mkDTExprs` instead. - -The argument `fvarInContext` allows you to specify which free variables in `e` will still be -in the context when the `RefinedDiscrTree` is being used for lookup. -It should return true only if the `RefinedDiscrTree` is built and used locally. -/ -def mkDTExpr (e : Expr) - (fvarInContext : FVarId → Bool := fun _ => false) : MetaM DTExpr := - withReducible do (MkDTExpr.mkDTExprAux e true |>.run {fvarInContext}) - -/-- Similar to `mkDTExpr`. -Return all encodings of `e` as a `DTExpr`, taking potential further η-reductions into account. -/ -def mkDTExprs (e : Expr) (onlySpecific : Bool) - (fvarInContext : FVarId → Bool := fun _ => false) : MetaM (List DTExpr) := + return { entry with stack := .expr (← mkExprInfo a bvars) :: entry.stack } + | _ => stackArgs entry + +/-- A single step in evaluating a `LazyEntry`. Allow multiple different outcomes. -/ +def evalLazyEntry (entry : LazyEntry) (eta : Bool) : + MetaM (Option (List (Key × LazyEntry))) := do + if let key :: computedKeys := entry.computedKeys then + -- If there is already a result available, use it. + return some [(key, { entry with computedKeys })] + else withMCtx entry.mctx do + let entry ← processPrevious entry + evalLazyEntryAux entry eta + +/-- Return all encodings of `e` as a `Array Key`. This is used for testing. -/ +partial def encodeExprWithEta (e : Expr) (labelledStars : Bool) : MetaM (Array (Array Key)) := withReducible do - let es ← (MkDTExpr.mkDTExprsAux e true).run' {} |>.run {fvarInContext} - return if onlySpecific then es.filter (·.isSpecific) else es + let entries ← (encodingStepWithEta e true (← mkInitLazyEntry labelledStars)).run { bvars := [] } + let entries := entries.map fun (key, entry) => (#[key], entry) + go entries.toArray #[] +where + /-- The main loop for `encodeExpr`. -/ + go (todo : Array (Array Key × LazyEntry)) (result : Array (Array Key)) : + MetaM (Array (Array Key)) := do + if h : todo.size = 0 then + return result + else -- use an if-then-else instead of if-then-return, so that `go` is tail recursive + let (keys, entry) := todo.back + let todo := todo.pop + match ← evalLazyEntry entry true with + | some xs => + let rec /-- + This variation on `List.fold` ensures that the array `keys` + isn't copied unnecessarily. -/ + fold xs todo := + match xs with + | [] => todo + | (key, entry) :: [] => todo.push (keys.push key, entry) + | (key, entry) :: xs => fold xs (todo.push (keys.push key, entry)) + go (fold xs todo) result + | none => + go todo (result.push keys) + +/-- Completely evaluate a `LazyEntry`. -/ +partial def LazyEntry.toList (entry : LazyEntry) (result : List Key := []) : MetaM (List Key) := do + match ← evalLazyEntry entry false with + | some [(key, entry')] => entry'.toList (key :: result) + | some _ => panic! "`evalLazyEntry` with `eta := false` can only give a singleton list" + | none => return result.reverse + +/-- Return the canonical encoding of `e` as a `Array Key`. +This is used for looking up `e` in a `RefinedDiscrTree`. -/ +def encodeExpr (e : Expr) (labelledStars : Bool) : MetaM (Key × List Key) := withReducible do + let (key, entry) ← initializeLazyEntry e labelledStars + return (key, ← entry.toList) end Lean.Meta.RefinedDiscrTree diff --git a/Mathlib/Lean/Meta/RefinedDiscrTree/Initialize.lean b/Mathlib/Lean/Meta/RefinedDiscrTree/Initialize.lean new file mode 100644 index 00000000000000..d0218555cc8715 --- /dev/null +++ b/Mathlib/Lean/Meta/RefinedDiscrTree/Initialize.lean @@ -0,0 +1,306 @@ +/- +Copyright (c) 2024 Jovan Gerbscheid. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jovan Gerbscheid +-/ +import Mathlib.Lean.Meta.RefinedDiscrTree.Basic +import Lean.Meta.LazyDiscrTree + +/-! +# Constructing a RefinedDiscrTree + +`RefinedDiscrTree` is lazy, so to add an entry, we need to compute +the first `Key` and a `LazyEntry`. These are computed by `initializeLazyEntry`. + +We provide `RefinedDiscrTree.insert` for directly performing this insert. + +For initializing a `RefinedDiscrTree` using all imported constants, +we provide `createImportedDiscrTree`, which loops through all imported constants, +and does this with a parallel computation. + +There is also `createModuleDiscrTree` which does the same but with the constants +from the current file. + +-/ +namespace Lean.Meta.RefinedDiscrTree + +variable {α : Type} + +/-- Directly insert a `Key`, `LazyEntry` pair into a `RefinedDiscrTree`. -/ +def insert (d : RefinedDiscrTree α) (key : Key) (entry : LazyEntry × α) : RefinedDiscrTree α := + if let some trie := d.root[key]? then + { d with + tries := d.tries.modify trie fun node => { node with pending := node.pending.push entry } } + else + { d with + root := d.root.insert key d.tries.size + tries := d.tries.push <| .node #[] none {} {} #[entry] } + +/-- +Structure for quickly initializing a lazy discrimination tree with a large number +of elements using concurrent functions for generating entries. +-/ +structure PreDiscrTree (α : Type) where + /-- Maps keys to index in tries array. -/ + root : Std.HashMap Key Nat := {} + /-- Lazy entries for root of trie. -/ + tries : Array (Array (LazyEntry × α)) := #[] + deriving Inhabited + +namespace PreDiscrTree + +@[specialize] +private def modifyAt (d : PreDiscrTree α) (k : Key) + (f : Array (LazyEntry × α) → Array (LazyEntry × α)) : PreDiscrTree α := + let { root, tries } := d + match root[k]? with + | none => + { root := root.insert k tries.size, tries := tries.push (f #[]) } + | some i => + { root, tries := tries.modify i f } + +/-- Add an entry to the pre-discrimination tree. -/ +def push (d : PreDiscrTree α) (k : Key) (e : LazyEntry × α) : PreDiscrTree α := + d.modifyAt k (·.push e) + +/-- Convert a pre-discrimination tree to a `RefinedDiscrTree`. -/ +def toRefinedDiscrTree (d : PreDiscrTree α) : RefinedDiscrTree α := + let { root, tries } := d + { root, tries := tries.map fun pending => .node #[] none {} {} pending } + +/-- Merge two discrimination trees. -/ +def append (x y : PreDiscrTree α) : PreDiscrTree α := + let (x, y, f) := + if x.root.size ≥ y.root.size then + (x, y, fun y x => x ++ y) + else + (y, x, fun x y => x ++ y) + let { root := yk, tries := ya } := y + yk.fold (init := x) fun d k yi => d.modifyAt k (f ya[yi]!) + +instance : Append (PreDiscrTree α) where + append := PreDiscrTree.append + +end PreDiscrTree + + +/-- Information about a failed import. -/ +private structure ImportFailure where + /-- Module with constant that import failed on. -/ + module : Name + /-- Constant that import failed on. -/ + const : Name + /-- Exception that triggers error. -/ + exception : Exception + +/-- Information generation from imported modules. -/ +private structure ImportErrorData where + errors : IO.Ref (Array ImportFailure) + +private def ImportErrorData.new : BaseIO ImportErrorData := do + return { errors := ← IO.mkRef #[] } +/-- +Add the entries generated by `act name constInfo` to the `PreDiscrTree`. + +Note: It is expensive to create two new `IO.Ref`s for every `MetaM` operation, + so instead we reuse the same refs `mstate` and `cstate`. These are also used to + remember the cache, and the namegenerator across the operations. +-/ +@[inline] private def addConstToPreDiscrTree + (cctx : Core.Context) + (env : Environment) + (modName : Name) + (data : ImportErrorData) + (mstate : IO.Ref Meta.State) + (cstate : IO.Ref Core.State) + (act : Name → ConstantInfo → MetaM (List (α × List (Key × LazyEntry)))) + (tree : PreDiscrTree α) (name : Name) (constInfo : ConstantInfo) : + BaseIO (PreDiscrTree α) := do + -- here we use an if-then-else clause instead of the more stylish if-then-return, + -- because it compiles to more performant code + if constInfo.isUnsafe then pure tree else + if LazyDiscrTree.blacklistInsertion env name then pure tree else + /- For efficiency, we leave it up to the implementation of `act` to reset the states if needed -/ + -- mstate.modify fun s => { cache := s.cache } + -- cstate.modify fun s => { env := s.env, cache := s.cache, ngen := s.ngen } + let mctx := { config := { transparency := .reducible } } + match ← (((act name constInfo) mctx mstate) cctx cstate).toBaseIO with + | .ok a => + return a.foldl (fun t (val, entries) => + entries.foldl (fun t (key, entry) => t.push key (entry, val)) t) tree + | .error e => + let i : ImportFailure := { + module := modName, + const := name, + exception := e } + data.errors.modify (·.push i) + return tree + + +/-- +Contains the pre discrimination tree and any errors occuring during initialization of +the library search tree. +-/ +private structure InitResults (α : Type) where + tree : PreDiscrTree α := {} + errors : Array ImportFailure := #[] + +namespace InitResults + +/-- Combine two initial results. -/ +protected def append (x y : InitResults α) : InitResults α := + let { tree := xv, errors := xe } := x + let { tree := yv, errors := ye } := y + { tree := xv ++ yv, errors := xe ++ ye } + +instance : Append (InitResults α) where + append := InitResults.append + +end InitResults + +private def toInitResults (data : ImportErrorData) (tree : PreDiscrTree α) : + BaseIO (InitResults α) := do + let de ← data.errors.swap #[] + pure ⟨tree, de⟩ + +/-- +Loop through all constants that appear in the module `mdata`, +and add the entries generated by `act` to the `PreDiscrTree`. +-/ +private partial def loadImportedModule + (cctx : Core.Context) + (env : Environment) + (data : ImportErrorData) + (mstate : IO.Ref Meta.State) + (cstate : IO.Ref Core.State) + (act : Name → ConstantInfo → MetaM (List (α × List (Key × LazyEntry)))) + (mname : Name) + (mdata : ModuleData) + (tree : PreDiscrTree α) + (i : Nat := 0) : BaseIO (PreDiscrTree α) := do + if h : i < mdata.constNames.size then + let name := mdata.constNames[i] + let constInfo := mdata.constants[i]! + let state ← addConstToPreDiscrTree cctx env mname data mstate cstate act tree name constInfo + loadImportedModule cctx env data mstate cstate act mname mdata state (i+1) + else + return tree + +/-- +Loop through all constants that appear in the modules with module index from `start` to `stop - 1`, +and add the entries generated by `act` to the `PreDiscrTree`. +-/ +private def createImportInitResults (cctx : Core.Context) (ngen : NameGenerator) + (env : Environment) (act : Name → ConstantInfo → MetaM (List (α × List (Key × LazyEntry)))) + (capacity start stop : Nat) : BaseIO (InitResults α) := do + let tree := { root := .emptyWithCapacity capacity } + go start stop tree (← ImportErrorData.new) (← IO.mkRef {}) (← IO.mkRef { env, ngen }) +where + go (start stop : Nat) (tree : PreDiscrTree α) + (data : ImportErrorData) + (mstate : IO.Ref Meta.State) + (cstate : IO.Ref Core.State) : + BaseIO (InitResults α) := do + if start < stop then + let mname := env.header.moduleNames[start]! + let mdata := env.header.moduleData[start]! + let tree ← loadImportedModule cctx env data mstate cstate act mname mdata tree + go (start+1) stop tree data mstate cstate + else + toInitResults data tree + termination_by stop - start + +private def getChildNgen : CoreM NameGenerator := do + let ngen ← getNGen + let (cngen, ngen) := ngen.mkChild + setNGen ngen + pure cngen + +private def logImportFailure (f : ImportFailure) : CoreM Unit := + logError m!"Processing failure with {f.const} in {f.module}:\n {f.exception.toMessageData}" + +/-- +Create a `RefinedDiscrTree` consisting of all entries generated by `act` +from imported constants. (it gets called by `addConstToPreDiscrTree`). +This uses parallel computation. +-/ +def createImportedDiscrTree (ngen : NameGenerator) (env : Environment) + (act : Name → ConstantInfo → MetaM (List (α × List (Key × LazyEntry)))) + (constantsPerTask capacityPerTask : Nat) : + CoreM (RefinedDiscrTree α) := do + let numModules := env.header.moduleData.size + let cctx ← read + let rec + /-- Allocate constants to tasks according to `constantsPerTask`. -/ + go (ngen : NameGenerator) (tasks : Array (Task (InitResults α))) (start cnt idx : Nat) := do + if h : idx < numModules then + let mdata := env.header.moduleData[idx] + let cnt := cnt + mdata.constants.size + if cnt > constantsPerTask then + let (childNGen, ngen) := ngen.mkChild + let t ← (createImportInitResults + cctx childNGen env act capacityPerTask start (idx+1)).asTask + go ngen (tasks.push t) (idx+1) 0 (idx+1) + else + go ngen tasks start cnt (idx+1) + else + if start < numModules then + let (childNGen, _) := ngen.mkChild + let t ← (createImportInitResults + cctx childNGen env act capacityPerTask start numModules).asTask + pure (tasks.push t) + else + pure tasks + termination_by env.header.moduleData.size - idx + let tasks ← go ngen #[] 0 0 0 + let r : InitResults α := tasks.foldl (init := {}) (· ++ ·.get) + r.errors.forM logImportFailure + return r.tree.toRefinedDiscrTree + +/-- +A discriminator tree for the current module's declarations only. + +Note. We use different discrimination trees for imported and current module +declarations since imported declarations are typically much more numerous but +not changed while the current module is edited. +-/ +structure ModuleDiscrTreeRef (α : Type _) where + /-- The reference to the `RefinedDiscrTree`. -/ + ref : IO.Ref (RefinedDiscrTree α) + +private def createModulePreDiscrTree + (cctx : Core.Context) + (ngen : NameGenerator) + (env : Environment) + (act : Name → ConstantInfo → MetaM (List (α × List (Key × LazyEntry)))) : + BaseIO (InitResults α) := do + let modName := env.header.mainModule + let data ← ImportErrorData.new + let r ← env.constants.map₂.foldlM (init := {}) (addConstToPreDiscrTree + cctx env modName data (← IO.mkRef {}) (← IO.mkRef { env, ngen }) act) + toInitResults data r + +/-- +Create a `RefinedDiscrTree` for current module declarations, consisting of all +entries generated by `act` from constants in the current file. +(it gets called by `addConstToPreDiscrTree`) +-/ +def createModuleDiscrTree (act : Name → ConstantInfo → MetaM (List (α × List (Key × LazyEntry)))) : + CoreM (RefinedDiscrTree α) := do + let env ← getEnv + let ngen ← getChildNgen + let ctx ← readThe Core.Context + let { tree, errors } ← createModulePreDiscrTree ctx ngen env act + errors.forM logImportFailure + return tree.toRefinedDiscrTree + +/-- +Create a reference for a `RefinedDiscrTree` for current module declarations. +-/ +def createModuleTreeRef (act : Name → ConstantInfo → MetaM (List (α × List (Key × LazyEntry)))) : + MetaM (ModuleDiscrTreeRef α) := do + profileitM Exception "build module discriminator tree" (← getOptions) do + let t ← createModuleDiscrTree act + pure { ref := ← IO.mkRef t } + +end Lean.Meta.RefinedDiscrTree diff --git a/Mathlib/Lean/Meta/RefinedDiscrTree/Lookup.lean b/Mathlib/Lean/Meta/RefinedDiscrTree/Lookup.lean index f70e6157957289..50ae0f916e9db9 100644 --- a/Mathlib/Lean/Meta/RefinedDiscrTree/Lookup.lean +++ b/Mathlib/Lean/Meta/RefinedDiscrTree/Lookup.lean @@ -1,187 +1,281 @@ /- -Copyright (c) 2023 Jovan Gerbscheid. All rights reserved. +Copyright (c) 2024 Jovan Gerbscheid. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Jovan Gerbscheid -/ import Mathlib.Lean.Meta.RefinedDiscrTree.Encode -/-! ## Matching with a RefinedDiscrTree +/-! +# Matching with a RefinedDiscrTree -We use a very simple unification algorithm. For all star/metavariable patterns in the -`RefinedDiscrTree` and in the target, we store the assignment, and when it is assigned again, -we check that it is the same assignment. --/ +This file defines the matching procedure for the `RefinedDiscrTree`. + +The main definitions are +* The structure `MatchResult`, which contains the match results, ordered by matching score. +* The (private) function `evalNode` which evaluates a node of the `RefinedDiscrTree` +* The (private) function `getMatchLoop`, which is the main function that computes the matches. + It implements the non-deterministic computation by keeping a stack of `PartialMatch`es, + and repeatedly processing the most recent one. +* The matching function `getMatch` that also returns an updated `RefinedDiscrTree` + +To find the matches, we first encode the expression as a `List Key`. Then using this, +we find all matches with the tree. When `unify == true`, we also allow metavariables in the target +expression to be assigned. -open Mathlib.Meta.FunProp (StateListT StateListM) +We use a simple unification algorithm. For all star/metavariable patterns in the +`RefinedDiscrTree` (and in the target if `unify == true`), we store the assignment, +and when it is attempted to be assigned again, we check that it is the same assignment. + +-/ namespace Lean.Meta.RefinedDiscrTree -variable {α} -namespace GetUnify +variable {α β : Type} -/-- If `k` is a key in `children`, return the corresponding `Trie α`. Otherwise return `none`. -/ -def findKey (children : Array (Key × Trie α)) (k : Key) : Option (Trie α) := - (·.2) <$> children.binSearch (k, default) (fun a b => a.1 < b.1) +/-- Monad for working with a `RefinedDiscrTree`. -/ +private abbrev TreeM α := StateRefT (Array (Trie α)) MetaM -private structure Context where - unify : Bool +/-- Run a `TreeM` computation using a `RefinedDiscrTree`. -/ +private def runTreeM (d : RefinedDiscrTree α) (m : TreeM α β) : + MetaM (β × RefinedDiscrTree α) := do + let { tries, root } := d + let (result, tries) ← withReducible <| m.run tries + pure (result, { tries, root }) -private structure State where - /-- Score representing how good the match is. -/ - score : Nat := 0 - /-- Metavariable assignments for the `Key.star` patterns in the `RefinedDiscrTree`. -/ - starAssignments : Std.HashMap Nat DTExpr := {} - /-- Metavariable assignments for the `Expr.mvar` in the expression. -/ - mvarAssignments : Std.HashMap MVarId (Array Key) := {} +private def setTrie (i : TrieIndex) (v : Trie α) : TreeM α Unit := + modify (·.set! i v) +/-- Create a new trie with the given lazy entry. -/ +private def newTrie (e : LazyEntry × α) : TreeM α TrieIndex := do + modifyGet fun a => (a.size, a.push (.node #[] none {} {} #[e])) -private abbrev M := ReaderT Context <| StateListM State +/-- Add a lazy entry to an existing trie. -/ +private def addLazyEntryToTrie (i : TrieIndex) (e : LazyEntry × α) : TreeM α Unit := + modify (·.modify i fun node => { node with pending := node.pending.push e }) -/-- Return all values from `x` in an array, together with their scores. -/ -private def M.run (unify : Bool) (x : M (Trie α)) : - Array (Array α × Nat) := - ((x.run { unify }).run {}).toArray.map (fun (t, s) => (t.values!, s.score)) +/-- +Evaluate the `Trie α` at index `trie`, +replacing it with the evaluated value, +and returning the `Trie α`. +-/ +private def evalNode (trie : TrieIndex) : TreeM α (Trie α) := do + let node := (← get)[trie]! + if node.pending.isEmpty then + return node + setTrie trie default -- reduce the reference count to `node` to be 1 + let mut { values, star, labelledStars, children, pending } := node + for (entry, value) in pending do + let some newEntries ← evalLazyEntry entry true | values := values.push value + for (key, entry) in newEntries do + let entry := (entry, value) + match key with + | .labelledStar label => + if let some trie := labelledStars[label]? then + addLazyEntryToTrie trie entry + else + labelledStars := labelledStars.insert label (← newTrie entry) + | .star => + if let some trie := star then + addLazyEntryToTrie trie entry + else + star := some (← newTrie entry) + | _ => + if let some trie := children[key]? then + addLazyEntryToTrie trie entry + else + children := children.insert key (← newTrie entry) + let node := { values, star, labelledStars, children, pending := #[] } + setTrie trie node + return node -/-- Increment the score by `n`. -/ -private def incrementScore (n : Nat) : M Unit := - modify fun s => { s with score := s.score + n } -/-- Log a metavariable assignment in the `State`. -/ -private def insertStarAssignment (n : Nat) (e : DTExpr) : M Unit := - modify fun s => { s with starAssignments := s.starAssignments.insert n e } +/-- +A match result contains the results from matching a term against +patterns in the discrimination tree. +-/ +structure MatchResult (α : Type) where + /-- + The elements in the match result. + + The `Nat` in the tree map represents the `score` of the results. + The elements are arrays of arrays, where each sub-array corresponds to one discr tree pattern. + -/ + elts : Std.TreeMap Nat (Array (Array α)) := {} + deriving Inhabited + +private def MatchResult.push (mr : MatchResult α) (score : Nat) (e : Array α) : MatchResult α := + { elts := mr.elts.alter score fun | some arr => arr.push e | none => #[e] } + +/-- +Convert a `MatchResult` into a `Array`, with better matches at the start of the array. +-/ +def MatchResult.toArray (mr : MatchResult α) : Array α := + mr.elts.foldr (init := #[]) fun _ a r => a.foldl (init := r) (· ++ ·) + +/- +A partial match captures the intermediate state of a match execution. + +N.B. Discrimination tree matching has non-determinism due to stars, +so the matching loop maintains a stack of partial match results. +-/ +private structure PartialMatch where + /-- Remaining terms to match -/ + keys : List Key + /-- Number of non-star matches so far -/ + score : Nat + /-- Trie to match next -/ + trie : TrieIndex + /-- Metavariable assignments for `.labelledStar` patterns in the discrimination tree. + We use a `List Key`, in the reverse order. -/ + treeStars : Std.HashMap Nat (List Key) := {} + deriving Inhabited -/-- Log a metavariable assignment in the `State`. -/ -private def assignMVar (mvarId : MVarId) (e : Array Key) : M Unit := do - let { mvarAssignments, .. } ← get - match mvarAssignments[mvarId]? with - | some e' => guard (e == e') - | none => - modify fun s => { s with mvarAssignments := s.mvarAssignments.insert mvarId e } -/-- Return the possible `Trie α` that match with `n` metavariable. -/ -partial def skipEntries (t : Trie α) (skipped : Array Key) : Nat → M (Array Key × Trie α) - | 0 => pure (skipped, t) +/-- +Add to the `todo` stack all matches that result from a `.star` in the query expression. +-/ +private partial def matchQueryStar (trie : TrieIndex) (pMatch : PartialMatch) + (todo : Array PartialMatch) (skip : Nat := 1) : TreeM α (Array PartialMatch) := do + match skip with | skip+1 => - t.children!.foldr (init := failure) fun (k, c) x => - (skipEntries c (skipped.push k) (skip + k.arity)) <|> x -/-- Return the possible `Trie α` that match with anything. -We add 1 to the matching score when the key is `.opaque`, -since this pattern is "harder" to match with. -/ -def matchTargetStar (mvarId? : Option MVarId) (t : Trie α) : M (Trie α) := do - let (keys, t) ← t.children!.foldr (init := failure) fun (k, c) x => (do - if k == .opaque then - incrementScore 1 - skipEntries c #[k] k.arity - ) <|> x - if let some mvarId := mvarId? then - assignMVar mvarId keys - return t - -/-- Return the possible `Trie α` that come from a `Key.star`, -while keeping track of the `Key.star` assignments. -/ -def matchTreeStars (e : DTExpr) (t : Trie α) : M (Trie α) := do - let {starAssignments, ..} ← get - let mut result := failure - /- The `Key.star` are at the start of the `t.children!`, - so this loops through all of them. -/ - for (k, c) in t.children! do - let .star i := k | break - if let some assignment := starAssignments[i]? then - if e == assignment then - result := (incrementScore e.size *> pure c) <|> result - else - result := (insertStarAssignment i e *> pure c) <|> result - result - -mutual - /-- Return the possible `Trie α` that match with `e`. -/ - partial def matchExpr (e : DTExpr) (t : Trie α) : M (Trie α) := do - if let .star mvarId? := e then - if (← read).unify then - matchTargetStar mvarId? t + let { star, labelledStars, children, .. } ← evalNode trie + let mut todo := todo + if let some trie := star then + todo ← matchQueryStar trie pMatch todo skip + todo ← labelledStars.foldM (init := todo) fun todo _ trie => + matchQueryStar trie pMatch todo skip + todo ← children.foldM (init := todo) fun todo key trie => + matchQueryStar trie pMatch todo (skip + key.arity) + return todo + | 0 => + return todo.push { pMatch with trie } + +/-- Return every value that is indexed in the tree. -/ +private def matchEverything (tree : RefinedDiscrTree α) : TreeM α (MatchResult α) := do + let pMatches ← tree.root.foldM (init := #[]) fun todo key trie => + matchQueryStar trie { keys := [], score := 0, trie := 0 } todo key.arity + pMatches.foldlM (init := {}) fun result pMatch => do + let { values, .. } ← evalNode pMatch.trie + return result.push (score := 0) values + +/-- Add to the `todo` stack all matches that result from a `.star _` in the discrimination tree. -/ +private partial def matchTreeStars (key : Key) (node : Trie α) (pMatch : PartialMatch) + (todo : Array PartialMatch) (unify : Bool) : Array PartialMatch := Id.run do + let { star, labelledStars, .. } := node + if labelledStars.isEmpty && star.isNone then + todo + else + let (dropped, keys) := drop [key] pMatch.keys key.arity + let mut todo := todo + if let some trie := star then + todo := todo.push { pMatch with keys, trie } + todo := node.labelledStars.fold (init := todo) fun todo id trie => + if let some assignment := pMatch.treeStars[id]? then + let eq lhs rhs := if unify then (isEq lhs.reverse rhs.reverse).isSome else lhs == rhs + if eq dropped assignment then + todo.push { pMatch with keys, trie, score := pMatch.score + dropped.length } + else + todo else - matchTreeStars e t - else - matchTreeStars e t <|> exactMatch e (findKey t.children!) - - /-- If `e` is not a metavariable, return the possible `Trie α` that exactly match with `e`. -/ - @[specialize] - partial def exactMatch (e : DTExpr) (find? : Key → Option (Trie α)) : M (Trie α) := do - - let findKey (k : Key) (x : Trie α → M (Trie α) := pure) (score := 1) : M (Trie α) := - match find? k with - | none => failure - | some trie => do - incrementScore score - x trie - - let matchArgs (args : Array DTExpr) : Trie α → M (Trie α) := - args.foldlM (fun t e => matchExpr e t) - - match e with - | .opaque => failure - | .const c args => findKey (.const c args.size) (matchArgs args) - | .fvar fvarId args => findKey (.fvar fvarId args.size) (matchArgs args) - | .bvar i args => findKey (.bvar i args.size) (matchArgs args) - | .lit v => findKey (.lit v) - | .sort => findKey .sort - | .lam b => findKey .lam (matchExpr b) 0 - | .forall d b => findKey .forall (matchExpr d >=> matchExpr b) - | .proj n i a args => findKey (.proj n i args.size) (matchExpr a >=> matchArgs args) - | _ => unreachable! - -end - -private partial def getMatchWithScoreAux (d : RefinedDiscrTree α) (e : DTExpr) (unify : Bool) - (allowRootStar : Bool := false) : Array (Array α × Nat) := (do - if e matches .star _ then - guard allowRootStar - d.root.foldl (init := failure) fun x k c => (do - if k == Key.opaque then - GetUnify.incrementScore 1 - let (_, t) ← GetUnify.skipEntries c #[k] k.arity - return t) <|> x + let treeStars := pMatch.treeStars.insert id dropped + todo.push { pMatch with keys, trie, treeStars } + return todo +where + /-- Drop the keys corresponding to the next `n` expressions. -/ + drop (dropped rest : List Key) (n : Nat) : (List Key × List Key) := Id.run do + match n with + | 0 => (dropped, rest) + | n+1 => + let key :: rest := rest | panic! "too few keys" + drop (key :: dropped) rest (n + key.arity) + + isEq (lhs rhs : List Key) : Option (List Key × List Key) := do + match lhs with + | [] => panic! "too few keys" + | .star :: lhs => + let (_, rhs) := drop [] rhs 1 + return (lhs, rhs) + | lHead :: lhs => + match rhs with + | [] => panic! "too few keys" + | .star :: rhs => + let (_, lhs) := drop [] lhs 1 + return (lhs, rhs) + | rHead :: rhs => + guard (lHead == rHead) + lHead.arity.foldM (init := (lhs, rhs)) fun _ _ (lhs, rhs) => isEq lhs rhs + +/-- Add to the `todo` stack the match with `key`. -/ +private def matchKey (key : Key) (children : Std.HashMap Key TrieIndex) (pMatch : PartialMatch) + (todo : Array PartialMatch) : Array PartialMatch := + if key == .opaque then todo else + match children[key]? with + | none => todo + | some trie => todo.push { pMatch with trie, score := pMatch.score + 1 } + +/-- Return the possible `Trie α` that match with `keys`. -/ +private partial def getMatchLoop (todo : Array PartialMatch) (result : MatchResult α) + (unify : Bool) : TreeM α (MatchResult α) := do + if h : todo.size = 0 then + return result else - GetUnify.exactMatch e d.root.find? - <|> do - guard allowRootStar - let some c := d.root.find? (.star 0) | failure - return c - ).run unify + let pMatch := todo.back + let todo := todo.pop + let node ← evalNode pMatch.trie + match pMatch.keys with + | [] => + getMatchLoop todo (result.push (score := pMatch.score) node.values) unify + | key :: keys => + let pMatch := { pMatch with keys } + match key with + -- `key` is not a `.labelledStar` + | .star => + if unify then + let todo ← matchQueryStar pMatch.trie pMatch todo + getMatchLoop todo result unify + else + let todo := matchTreeStars key node pMatch todo unify + getMatchLoop todo result unify + | _ => + let todo := matchTreeStars key node pMatch todo unify + let todo := matchKey key node.children pMatch todo + getMatchLoop todo result unify -end GetUnify +/-- Return the results from matching the pattern `[.star]` or `[.labelledStar 0]`. -/ +private def matchTreeRootStar (root : Std.HashMap Key TrieIndex) : TreeM α (MatchResult α) := do + let mut result := {} + if let some trie := root[Key.labelledStar 0]? then + let { values, .. } ← evalNode trie + result := result.push (score := 0) values + if let some trie := root[Key.star]? then + let { values, .. } ← evalNode trie + result := result.push (score := 0) values + return result /-- -Return the results from the `RefinedDiscrTree` that match the given expression, -together with their matching scores, in decreasing order of score. - -Each entry of type `Array α × Nat` corresponds to one pattern. - -If `unify := false`, then metavariables in `e` are treated as opaque variables. -This is for when you don't want to instantiate metavariables in `e`. - -If `allowRootStar := false`, then we don't allow `e` or the matched key in `d` -to be a star pattern. -/ -def getMatchWithScore (d : RefinedDiscrTree α) (e : Expr) (unify : Bool) - (allowRootStar : Bool := false) : MetaM (Array (Array α × Nat)) := do - let e ← mkDTExpr e - let result := GetUnify.getMatchWithScoreAux d e unify allowRootStar - return result.qsort (·.2 > ·.2) - -/-- Similar to `getMatchWithScore`, but also returns matches with prefixes of `e`. -We store the score, followed by the number of ignored arguments. -/ -partial def getMatchWithScoreWithExtra (d : RefinedDiscrTree α) (e : Expr) (unify : Bool) - (allowRootStar : Bool := false) : - MetaM (Array (Array α × Nat × Nat)) := do - let result ← go e 0 - return result.qsort (·.2.1 > ·.2.1) -where - /-- go -/ - go (e : Expr) (numIgnored : Nat) : MetaM (Array (Array α × Nat × Nat)) := do - let result ← getMatchWithScore d e unify allowRootStar - let result := result.map fun (a, b) => (a, b, numIgnored) - match e with - | .app e _ => return (← go e (numIgnored + 1)) ++ result - | _ => return result +Find values that match `e` in `d`. +* If `unify == true` then metavarables in `e` can be assigned. +* If `matchRootStar == true` then we allow metavariables at the root to unify. + Set this to `false` in order to avoid too many results. +-/ +def getMatch (d : RefinedDiscrTree α) (e : Expr) (unify matchRootStar : Bool) : + MetaM (MatchResult α × RefinedDiscrTree α) := do + withReducible do runTreeM d do + let (key, keys) ← encodeExpr e (labelledStars := false) + let pMatch : PartialMatch := { keys, score := 0, trie := default } + if key == .star then + if matchRootStar then + if unify then + matchEverything d + else + matchTreeRootStar d.root + else + throwError m! "The expression {e} has pattern `*`, so we don't return any match results." + else + let todo := matchKey key d.root pMatch #[] + if matchRootStar then + getMatchLoop todo (← matchTreeRootStar d.root) unify + else + getMatchLoop todo {} unify end Lean.Meta.RefinedDiscrTree diff --git a/Mathlib/Lean/Meta/RefinedDiscrTree/Pi.lean b/Mathlib/Lean/Meta/RefinedDiscrTree/Pi.lean deleted file mode 100644 index f150b0e68fbc53..00000000000000 --- a/Mathlib/Lean/Meta/RefinedDiscrTree/Pi.lean +++ /dev/null @@ -1,119 +0,0 @@ -/- -Copyright (c) 2023 Jovan Gerbscheid. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Jovan Gerbscheid --/ -import Mathlib.Algebra.Notation.Pi -import Mathlib.Init - -/-! -# Reducing Pi instances for indexing in the RefinedDiscrTree --/ - --- This file is only concerned with notation, and should avoid importing the actual algebraic --- hierarchy. -assert_not_exists Monoid - -namespace Lean.Meta.RefinedDiscrTree - -variable {α} - -/-- Introduce new lambdas by η-expansion. -/ -@[specialize] -def etaExpand (args : Array Expr) (type : Expr) (lambdas : List FVarId) (goalArity : Nat) - (k : Array Expr → List FVarId → MetaM α) : MetaM α := do - if args.size < goalArity then - withLocalDeclD `_η type fun fvar => - etaExpand (args.push fvar) type (fvar.fvarId! :: lambdas) goalArity k - else - k args lambdas - -/-- Normalize an application of a heterogeneous binary operator like `HAdd.hAdd`, using: -- `f = fun x => f x` to increase the arity to 6 -- `(f + g) a = f a + g a` to decrease the arity to 6 -- `(fun x => f x + g x) = f + g` to get rid of any lambdas in front -/ -def reduceHBinOpAux (args : Array Expr) (lambdas : List FVarId) (instH instPi : Name) : - OptionT MetaM (Expr × Expr × Expr × List FVarId) := do - let some (mkApp2 (.const instH' _) type inst) := args[3]? | failure - guard (instH == instH') - if args.size ≤ 6 then - etaExpand args type lambdas 6 fun args lambdas => - distributeLambdas lambdas type args[4]! args[5]! - else - /- use that `(f + g) a = f a + g a` -/ - let mut type := type - let mut inst := inst - let mut lhs := args[4]! - let mut rhs := args[5]! - for arg in args[6:] do - let mkApp3 (.const i _) _ f inst' := inst | return (type, lhs, rhs, lambdas) - unless i == instPi do return (type, lhs, rhs, lambdas) - type := .app f arg - inst := inst' - lhs := .app lhs arg - rhs := .app rhs arg - distributeLambdas lambdas type lhs rhs -where - /-- use that `(fun x => f x + g x) = f + g` -/ - distributeLambdas (lambdas : List FVarId) (type lhs rhs : Expr) : - MetaM (Expr × Expr × Expr × List FVarId) := match lambdas with - | fvarId :: lambdas => do - let decl ← fvarId.getDecl - let type := .forallE decl.userName decl.type (type.abstract #[.fvar fvarId]) decl.binderInfo - let lhs := .lam decl.userName decl.type (lhs.abstract #[.fvar fvarId]) decl.binderInfo - let rhs := .lam decl.userName decl.type (rhs.abstract #[.fvar fvarId]) decl.binderInfo - distributeLambdas lambdas type lhs rhs - | [] => return (type, lhs, rhs, []) - -/-- Normalize an application if the head is `+`, `*`, `-` or `/`. -Optionally return the `(type, lhs, rhs, lambdas)`. -/ -@[inline] def reduceHBinOp (n : Name) (args : Array Expr) (lambdas : List FVarId) : - MetaM (Option (Expr × Expr × Expr × List FVarId)) := - match n with - | ``HAdd.hAdd => reduceHBinOpAux args lambdas ``instHAdd ``Pi.instAdd - | ``HMul.hMul => reduceHBinOpAux args lambdas ``instHMul ``Pi.instMul - | ``HSub.hSub => reduceHBinOpAux args lambdas ``instHSub ``Pi.instSub - | ``HDiv.hDiv => reduceHBinOpAux args lambdas ``instHDiv ``Pi.instDiv - | _ => return none - -/-- Normalize an application of a unary operator like `Inv.inv`, using: -- `f⁻¹ a = (f a)⁻¹` to decrease the arity to 3 -- `(fun x => (f a)⁻¹) = f⁻¹` to get rid of any lambdas in front -/ -def reduceUnOpAux (args : Array Expr) (lambdas : List FVarId) (instPi : Name) : - OptionT MetaM (Expr × Expr × List FVarId) := do - guard (args.size ≥ 3) - let mut type := args[0]! - let mut inst := args[1]! - let mut arg := args[2]! - if args.size == 3 then - distributeLambdas lambdas type arg - else - /- use that `f⁻¹ a = (f a)⁻¹` -/ - for arg' in args[3:] do - let mkApp3 (.const i _) _ f inst' := inst | return (type, arg, lambdas) - unless i == instPi do return (type, arg, lambdas) - type := .app f arg' - inst := inst' - arg := .app arg arg' - distributeLambdas lambdas type arg -where - /-- use that `(fun x => (f x)⁻¹) = f⁻¹` -/ - distributeLambdas (lambdas : List FVarId) (type arg : Expr) : - MetaM (Expr × Expr × List FVarId) := match lambdas with - | fvarId :: lambdas => do - let decl ← fvarId.getDecl - let type := .forallE decl.userName decl.type (type.abstract #[.fvar fvarId]) decl.binderInfo - let arg := .lam decl.userName decl.type (arg.abstract #[.fvar fvarId]) decl.binderInfo - distributeLambdas lambdas type arg - | [] => return (type, arg, []) - -/-- Normalize an application if the head is `⁻¹` or `-`. -Optionally return the `(type, arg, lambdas)`. -/ -@[inline] def reduceUnOp (n : Name) (args : Array Expr) (lambdas : List FVarId) : - MetaM (Option (Expr × Expr × List FVarId)) := - match n with - | ``Neg.neg => reduceUnOpAux args lambdas ``Pi.instNeg - | ``Inv.inv => reduceUnOpAux args lambdas ``Pi.instInv - | _ => return none - -end Lean.Meta.RefinedDiscrTree diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean index 42dfce9e132f82..b2de7a48b842ee 100644 --- a/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean +++ b/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean @@ -163,10 +163,22 @@ def symm (e : P₁ ≃ᵃ[k] P₂) : P₂ ≃ᵃ[k] P₁ where LinearEquiv.apply_symm_apply, Equiv.apply_symm_apply] @[simp] +theorem toEquiv_symm (e : P₁ ≃ᵃ[k] P₂) : e.symm.toEquiv = e.toEquiv.symm := + rfl + +@[deprecated "use instead `toEquiv_symm`, in the reverse direction" (since := "2025-06-08")] theorem symm_toEquiv (e : P₁ ≃ᵃ[k] P₂) : e.toEquiv.symm = e.symm.toEquiv := rfl @[simp] +theorem coe_symm_toEquiv (e : P₁ ≃ᵃ[k] P₂) : ⇑e.toEquiv.symm = e.symm := + rfl + +@[simp] +theorem linear_symm (e : P₁ ≃ᵃ[k] P₂) : e.symm.linear = e.linear.symm := + rfl + +@[deprecated "use instead `linear_symm`, in the reverse direction" (since := "2025-06-08")] theorem symm_linear (e : P₁ ≃ᵃ[k] P₂) : e.linear.symm = e.symm.linear := rfl @@ -355,8 +367,6 @@ def equivUnitsAffineMap : (P₁ ≃ᵃ[k] P₁) ≃* (P₁ →ᵃ[k] P₁)ˣ whe linear := LinearMap.GeneralLinearGroup.generalLinearEquiv _ _ <| Units.map AffineMap.linearHom u map_vadd' := fun _ _ => (u : P₁ →ᵃ[k] P₁).map_vadd _ _ } - left_inv _ := AffineEquiv.ext fun _ => rfl - right_inv _ := Units.ext <| AffineMap.ext fun _ => rfl map_mul' _ _ := rfl variable (k) diff --git a/Mathlib/LinearAlgebra/AffineSpace/Basis.lean b/Mathlib/LinearAlgebra/AffineSpace/Basis.lean index d8dde70abd1967..b8eeb4bb2401d4 100644 --- a/Mathlib/LinearAlgebra/AffineSpace/Basis.lean +++ b/Mathlib/LinearAlgebra/AffineSpace/Basis.lean @@ -31,7 +31,7 @@ barycentric coordinate of `q : P` is `1 - fᵢ (q -ᵥ p i)`. * `AffineBasis.coord_apply_ne`: the behaviour of `AffineBasis.coord i` on `p j` when `j ≠ i`. * `AffineBasis.coord_apply`: the behaviour of `AffineBasis.coord i` on `p j` for general `j`. * `AffineBasis.coord_apply_combination`: the characterisation of `AffineBasis.coord i` in terms - of affine combinations, i.e., `AffineBasis.coord i (w₀ p₀ + w₁ p₁ + ⋯) = wᵢ`. + of affine combinations, i.e., `AffineBasis.coord i (w₀ p₀ + w₁ p₁ + ⋯) = wᵢ`. ## TODO diff --git a/Mathlib/LinearAlgebra/AffineSpace/ContinuousAffineEquiv.lean b/Mathlib/LinearAlgebra/AffineSpace/ContinuousAffineEquiv.lean index 075aebb978e9bd..74fcec33a77d56 100644 --- a/Mathlib/LinearAlgebra/AffineSpace/ContinuousAffineEquiv.lean +++ b/Mathlib/LinearAlgebra/AffineSpace/ContinuousAffineEquiv.lean @@ -172,12 +172,25 @@ def symm (e : P₁ ≃ᴬ[k] P₂) : P₂ ≃ᴬ[k] P₁ where continuous_invFun := e.continuous_toFun @[simp] +theorem toAffineEquiv_symm (e : P₁ ≃ᴬ[k] P₂) : e.symm.toAffineEquiv = e.toAffineEquiv.symm := + rfl + +@[deprecated "use instead `toAffineEquiv_symm`, in the reverse direction" (since := "2025-06-08")] theorem symm_toAffineEquiv (e : P₁ ≃ᴬ[k] P₂) : e.toAffineEquiv.symm = e.symm.toAffineEquiv := rfl @[simp] +theorem coe_symm_toAffineEquiv (e : P₁ ≃ᴬ[k] P₂) : ⇑e.toAffineEquiv.symm = e.symm := rfl + +@[simp] +theorem toEquiv_symm (e : P₁ ≃ᴬ[k] P₂) : e.symm.toEquiv = e.toEquiv.symm := rfl + +@[deprecated "use instead `symm_toEquiv`, in the reverse direction" (since := "2025-06-08")] theorem symm_toEquiv (e : P₁ ≃ᴬ[k] P₂) : e.toEquiv.symm = e.symm.toEquiv := rfl +@[simp] +theorem coe_symm_toEquiv (e : P₁ ≃ᴬ[k] P₂) : ⇑e.toEquiv.symm = e.symm := rfl + @[simp] theorem apply_symm_apply (e : P₁ ≃ᴬ[k] P₂) (p : P₂) : e (e.symm p) = p := e.toEquiv.apply_symm_apply p diff --git a/Mathlib/LinearAlgebra/Alternating/Basic.lean b/Mathlib/LinearAlgebra/Alternating/Basic.lean index abcc86848379f1..34ae2d61286e09 100644 --- a/Mathlib/LinearAlgebra/Alternating/Basic.lean +++ b/Mathlib/LinearAlgebra/Alternating/Basic.lean @@ -387,7 +387,6 @@ and `1`-multilinear alternating maps from `M` to `N`. -/ def ofSubsingleton [Subsingleton ι] (i : ι) : (M →ₗ[R] N) ≃ (M [⋀^ι]→ₗ[R] N) where toFun f := ⟨MultilinearMap.ofSubsingleton R M N i f, fun _ _ _ _ ↦ absurd (Subsingleton.elim _ _)⟩ invFun f := (MultilinearMap.ofSubsingleton R M N i).symm f - left_inv _ := rfl right_inv _ := coe_multilinearMap_injective <| (MultilinearMap.ofSubsingleton R M N i).apply_symm_apply _ @@ -927,5 +926,4 @@ def AlternatingMap.constLinearEquivOfIsEmpty [IsEmpty ι] : N'' ≃ₗ[R'] (M'' map_add' _ _ := rfl map_smul' _ _ := rfl invFun f := f 0 - left_inv _ := rfl right_inv f := ext fun _ => AlternatingMap.congr_arg f <| Subsingleton.elim _ _ diff --git a/Mathlib/LinearAlgebra/ExteriorPower/Basic.lean b/Mathlib/LinearAlgebra/ExteriorPower/Basic.lean index c1c70f50e11fb2..c863d9eb482aad 100644 --- a/Mathlib/LinearAlgebra/ExteriorPower/Basic.lean +++ b/Mathlib/LinearAlgebra/ExteriorPower/Basic.lean @@ -137,8 +137,6 @@ noncomputable def relationsSolutionEquiv {ι : Type*} [DecidableEq ι] {M : Type · simp · simp · simpa using f.map_eq_zero_of_eq v hm hij } - left_inv _ := rfl - right_inv _ := rfl /-- The universal property of the exterior power. -/ noncomputable def isPresentationCore : diff --git a/Mathlib/LinearAlgebra/FreeModule/Determinant.lean b/Mathlib/LinearAlgebra/FreeModule/Determinant.lean index 678e3c2b8a8c23..3fa9050e31eec5 100644 --- a/Mathlib/LinearAlgebra/FreeModule/Determinant.lean +++ b/Mathlib/LinearAlgebra/FreeModule/Determinant.lean @@ -15,7 +15,7 @@ free (finite) modules over any commutative ring. ## Main results * `LinearMap.det_zero''`: The determinant of the constant zero map is zero, in a finite free -nontrivial module. + nontrivial module. -/ diff --git a/Mathlib/LinearAlgebra/FreeModule/Finite/Quotient.lean b/Mathlib/LinearAlgebra/FreeModule/Finite/Quotient.lean index 8aa919c243a2bc..bd39ca2c9dc15d 100644 --- a/Mathlib/LinearAlgebra/FreeModule/Finite/Quotient.lean +++ b/Mathlib/LinearAlgebra/FreeModule/Finite/Quotient.lean @@ -100,6 +100,16 @@ theorem finiteQuotientOfFreeOfRankEq [Module.Free ℤ M] [Module.Finite ℤ M] @[deprecated (since := "2025-03-15")] alias fintypeQuotientOfFreeOfRankEq := finiteQuotientOfFreeOfRankEq +theorem finiteQuotient_iff [Module.Free ℤ M] [Module.Finite ℤ M] (N : Submodule ℤ M) : + Finite (M ⧸ N) ↔ Module.finrank ℤ N = Module.finrank ℤ M := by + refine ⟨fun h ↦ le_antisymm (finrank_le N) <| + ((LinearMap.lsmul ℤ M (Nat.card (M ⧸ N))).codRestrict N + fun x ↦ ?_).finrank_le_finrank_of_injective ?_, fun h ↦ finiteQuotientOfFreeOfRankEq N h⟩ + · simpa using AddSubgroup.nsmul_index_mem N.toAddSubgroup x + · refine (LinearMap.lsmul_injective ?_).codRestrict _ + exact Int.ofNat_ne_zero.mpr <| Nat.card_ne_zero.mpr + ⟨Set.nonempty_iff_univ_nonempty.mpr Set.univ_nonempty, h⟩ + variable (F : Type*) [CommRing F] [Algebra F R] [Module F M] [IsScalarTower F R M] (b : Basis ι R M) {N : Submodule R M} diff --git a/Mathlib/LinearAlgebra/GeneralLinearGroup.lean b/Mathlib/LinearAlgebra/GeneralLinearGroup.lean index 289bcb15903b71..22fcc6b6b6de9d 100644 --- a/Mathlib/LinearAlgebra/GeneralLinearGroup.lean +++ b/Mathlib/LinearAlgebra/GeneralLinearGroup.lean @@ -54,8 +54,6 @@ equivalences between `M` and itself. -/ def generalLinearEquiv : GeneralLinearGroup R M ≃* M ≃ₗ[R] M where toFun := toLinearEquiv invFun := ofLinearEquiv - left_inv f := by ext; rfl - right_inv f := by ext; rfl map_mul' x y := by ext; rfl @[simp] diff --git a/Mathlib/LinearAlgebra/Matrix/AbsoluteValue.lean b/Mathlib/LinearAlgebra/Matrix/AbsoluteValue.lean index ae503c1edc6d6b..a224ef97c5de9d 100644 --- a/Mathlib/LinearAlgebra/Matrix/AbsoluteValue.lean +++ b/Mathlib/LinearAlgebra/Matrix/AbsoluteValue.lean @@ -42,7 +42,7 @@ theorem det_le {A : Matrix n n R} {abv : AbsoluteValue R S} {x : S} (hx : ∀ i _ ≤ ∑ σ : Perm n, abv (Perm.sign σ • ∏ i, A (σ i) i) := abv.sum_le _ _ _ = ∑ σ : Perm n, ∏ i, abv (A (σ i) i) := sum_congr rfl fun σ _ => by rw [abv.map_units_int_smul, abv.map_prod] - _ ≤ ∑ _σ : Perm n, ∏ _i : n, x := by gcongr <;> simp [hx] + _ ≤ ∑ _σ : Perm n, ∏ _i : n, x := by gcongr; simp [hx] _ = (Fintype.card n)! • x ^ Fintype.card n := by simp [Fintype.card_perm] theorem det_sum_le {ι : Type*} (s : Finset ι) {A : ι → Matrix n n R} {abv : AbsoluteValue R S} diff --git a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Card.lean b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Card.lean index 50ffd3ebc3452b..59ac5a8ea2e570 100644 --- a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Card.lean +++ b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Card.lean @@ -78,7 +78,6 @@ noncomputable def equiv_GL_linearindependent : rw [← Basis.coePiBasisFun.toMatrix_eq_transpose, ← coe_basisOfPiSpaceOfLinearIndependent M.2] exact isUnit_det_of_invertible _ - left_inv := fun _ ↦ Units.ext (ext fun _ _ ↦ rfl) right_inv := by exact congrFun rfl /-- The cardinal of the general linear group over a finite field. -/ diff --git a/Mathlib/LinearAlgebra/Matrix/ToLin.lean b/Mathlib/LinearAlgebra/Matrix/ToLin.lean index f34c0630f913e2..3fdc2c56bfab4a 100644 --- a/Mathlib/LinearAlgebra/Matrix/ToLin.lean +++ b/Mathlib/LinearAlgebra/Matrix/ToLin.lean @@ -300,7 +300,7 @@ def LinearMap.toMatrix' : ((n → R) →ₗ[R] m → R) ≃ₗ[R] Matrix m n R w /-- A `Matrix m n R` is linearly equivalent to a linear map `(n → R) →ₗ[R] (m → R)`. -Note that the forward-direction does not require `DecidableEq` and is `Matrix.vecMulLin`. -/ +Note that the forward-direction does not require `DecidableEq` and is `Matrix.mulVecLin`. -/ def Matrix.toLin' : Matrix m n R ≃ₗ[R] (n → R) →ₗ[R] m → R := LinearMap.toMatrix'.symm diff --git a/Mathlib/LinearAlgebra/Multilinear/Basic.lean b/Mathlib/LinearAlgebra/Multilinear/Basic.lean index 93bffc351d3fa2..0ff8ee8ce0266e 100644 --- a/Mathlib/LinearAlgebra/Multilinear/Basic.lean +++ b/Mathlib/LinearAlgebra/Multilinear/Basic.lean @@ -273,7 +273,6 @@ def ofSubsingleton [Subsingleton ι] (i : ι) : simpa [update_eq_const_of_subsingleton] using f.map_update_add 0 i x y map_smul' := fun c x ↦ by simpa [update_eq_const_of_subsingleton] using f.map_update_smul 0 i c x } - left_inv _ := rfl right_inv f := by ext x; refine congr_arg f ?_; exact (eq_const_of_subsingleton _ _).symm variable (M₁) {M₂} @@ -982,7 +981,6 @@ def constLinearEquivOfIsEmpty [IsEmpty ι] : M₂ ≃ₗ[S] MultilinearMap R M map_add' _ _ := rfl map_smul' _ _ := rfl invFun f := f 0 - left_inv _ := rfl right_inv f := ext fun _ => MultilinearMap.congr_arg f <| Subsingleton.elim _ _ /-- `MultilinearMap.domDomCongr` as a `LinearEquiv`. -/ diff --git a/Mathlib/LinearAlgebra/Multilinear/Curry.lean b/Mathlib/LinearAlgebra/Multilinear/Curry.lean index ad63ad34e2712a..9fcfc8e4c690c9 100644 --- a/Mathlib/LinearAlgebra/Multilinear/Curry.lean +++ b/Mathlib/LinearAlgebra/Multilinear/Curry.lean @@ -328,7 +328,6 @@ def currySumEquiv : MultilinearMap R N M₂ ≃ₗ[R] toFun := currySum invFun := uncurrySum left_inv _ := by simp - right_inv _ := rfl map_add' := by aesop map_smul' := by aesop diff --git a/Mathlib/LinearAlgebra/Prod.lean b/Mathlib/LinearAlgebra/Prod.lean index 9eb725e8ffaff9..bd8bac710e16f3 100644 --- a/Mathlib/LinearAlgebra/Prod.lean +++ b/Mathlib/LinearAlgebra/Prod.lean @@ -118,8 +118,6 @@ def prodEquiv [Module S M₂] [Module S M₃] [SMulCommClass R S M₂] [SMulComm ((M →ₗ[R] M₂) × (M →ₗ[R] M₃)) ≃ₗ[S] M →ₗ[R] M₂ × M₃ where toFun f := f.1.prod f.2 invFun f := ((fst _ _ _).comp f, (snd _ _ _).comp f) - left_inv f := by ext <;> rfl - right_inv f := by ext <;> rfl map_add' _ _ := rfl map_smul' _ _ := rfl @@ -568,7 +566,6 @@ def sndEquiv : Submodule.snd R M M₂ ≃ₗ[R] M₂ where rintro ⟨⟨x, y⟩, hx⟩ simp only [snd, comap_bot, mem_ker, fst_apply] at hx simpa only [Subtype.mk.injEq, Prod.mk.injEq, and_true] using hx.symm - right_inv := by rintro x; rfl theorem snd_map_fst : (Submodule.snd R M M₂).map (LinearMap.fst R M M₂) = ⊥ := by aesop (add simp snd) diff --git a/Mathlib/LinearAlgebra/Projection.lean b/Mathlib/LinearAlgebra/Projection.lean index 2b5d3bccabca45..6e97c137d33692 100644 --- a/Mathlib/LinearAlgebra/Projection.lean +++ b/Mathlib/LinearAlgebra/Projection.lean @@ -353,8 +353,6 @@ correspondence with linear maps to the submodule that restrict to the identity o invFun f := ⟨p.subtype ∘ₗ f.1, LinearMap.ext fun x ↦ by simp [f.2], le_antisymm ((range_comp_le_range _ _).trans_eq p.range_subtype) fun x hx ↦ ⟨x, Subtype.ext_iff.1 <| f.2 ⟨x, hx⟩⟩⟩ - left_inv _ := rfl - right_inv _ := rfl end Submodule diff --git a/Mathlib/LinearAlgebra/RootSystem/Defs.lean b/Mathlib/LinearAlgebra/RootSystem/Defs.lean index e4a16b5a3b4652..d9e1c6c934bb35 100644 --- a/Mathlib/LinearAlgebra/RootSystem/Defs.lean +++ b/Mathlib/LinearAlgebra/RootSystem/Defs.lean @@ -142,8 +142,6 @@ variable (ι R M N) in @[simps] def flipEquiv : RootPairing ι R N M ≃ RootPairing ι R M N where toFun P := P.flip invFun P := P.flip - left_inv _ := rfl - right_inv _ := rfl /-- If we interchange the roles of `M` and `N`, we still have a root system. -/ protected def _root_.RootSystem.flip (P : RootSystem ι R M N) : RootSystem ι R N M := @@ -161,8 +159,6 @@ variable (ι R M N) in @[simps] def _root_.RootSystem.flipEquiv : RootSystem ι R N M ≃ RootSystem ι R M N where toFun P := P.flip invFun P := P.flip - left_inv _ := rfl - right_inv _ := rfl lemma ne_zero [NeZero (2 : R)] : (P.root i : M) ≠ 0 := fun h ↦ NeZero.ne' (2 : R) <| by simpa [h] using P.root_coroot_two i diff --git a/Mathlib/LinearAlgebra/RootSystem/IsValuedIn.lean b/Mathlib/LinearAlgebra/RootSystem/IsValuedIn.lean index b76ca0c812a6bd..df69a744997522 100644 --- a/Mathlib/LinearAlgebra/RootSystem/IsValuedIn.lean +++ b/Mathlib/LinearAlgebra/RootSystem/IsValuedIn.lean @@ -243,8 +243,8 @@ lemma rootSpan_dualAnnihilator_map_eq_iInf_ker_root' : suffices (P.rootSpan R).dualAnnihilator.map P.toDualRight.symm = {x | ∀ i, P.root' i x = 0} from SetLike.coe_injective <| by ext; simp [this] ext x - rw [rootSpan, Submodule.map_coe, Submodule.coe_dualAnnihilator_span, ← EquivLike.coe_coe, - ← LinearEquiv.coe_toEquiv_symm, ← Equiv.setOf_apply_symm_eq_image_setOf, Equiv.symm_symm] + rw [rootSpan, Submodule.map_coe, Submodule.coe_dualAnnihilator_span, + ← LinearEquiv.coe_symm_toEquiv, ← Equiv.setOf_apply_symm_eq_image_setOf, Equiv.symm_symm] simp [Set.range_subset_iff] lemma corootSpan_dualAnnihilator_map_eq_iInf_ker_coroot' : diff --git a/Mathlib/LinearAlgebra/Span/Basic.lean b/Mathlib/LinearAlgebra/Span/Basic.lean index d648e9e6bac844..983af48487f59c 100644 --- a/Mathlib/LinearAlgebra/Span/Basic.lean +++ b/Mathlib/LinearAlgebra/Span/Basic.lean @@ -44,6 +44,11 @@ variable {F : Type*} [FunLike F M M₂] [SemilinearMapClass F σ₁₂ M M₂] variable {s t : Set M} +lemma _root_.AddSubmonoid.toNatSubmodule_closure (s : Set M) : + (AddSubmonoid.closure s).toNatSubmodule = .span ℕ s := + (Submodule.span_le.mpr AddSubmonoid.subset_closure).antisymm' + ((Submodule.span ℕ s).toAddSubmonoid.closure_le.mpr Submodule.subset_span) + /-- A version of `Submodule.span_eq` for when the span is by a smaller ring. -/ @[simp] theorem span_coe_eq_restrictScalars [Semiring S] [SMul S R] [Module S M] [IsScalarTower S R M] : @@ -429,6 +434,11 @@ section AddCommGroup variable [Ring R] [AddCommGroup M] [Module R M] +lemma _root_.AddSubgroup.toIntSubmodule_closure (s : Set M) : + (AddSubgroup.closure s).toIntSubmodule = .span ℤ s := + (Submodule.span_le.mpr AddSubgroup.subset_closure).antisymm' + ((Submodule.span ℤ s).toAddSubgroup.closure_le.mpr Submodule.subset_span) + @[simp] theorem span_neg (s : Set M) : span R (-s) = span R s := calc diff --git a/Mathlib/LinearAlgebra/TensorProduct/Basic.lean b/Mathlib/LinearAlgebra/TensorProduct/Basic.lean index 4ea48d92327e30..122be61e857d7a 100644 --- a/Mathlib/LinearAlgebra/TensorProduct/Basic.lean +++ b/Mathlib/LinearAlgebra/TensorProduct/Basic.lean @@ -566,9 +566,7 @@ with the property that its composition with the canonical bilinear map `M → N the given bilinear map `M → N → P`. -/ def lift.equiv : (M →ₗ[R] N →ₗ[R] P) ≃ₗ[R] M ⊗[R] N →ₗ[R] P := { uncurry R M N P with - invFun := fun f => (mk R M N).compr₂ f - left_inv := fun _ => LinearMap.ext₂ fun _ _ => lift.tmul _ _ - right_inv := fun _ => ext' fun _ _ => rfl } + invFun := fun f => (mk R M N).compr₂ f } @[simp] theorem lift.equiv_apply (f : M →ₗ[R] N →ₗ[R] P) (m : M) (n : N) : diff --git a/Mathlib/Logic/Embedding/Basic.lean b/Mathlib/Logic/Embedding/Basic.lean index bb4cc279f45e7e..eea5e71f0955a3 100644 --- a/Mathlib/Logic/Embedding/Basic.lean +++ b/Mathlib/Logic/Embedding/Basic.lean @@ -239,7 +239,6 @@ def oneEmbeddingEquiv {one α : Type*} [Unique one] : (one ↪ α) ≃ α where toFun := fun _ ↦ a inj' x y h := by simp [Unique.uniq inferInstance] } left_inv f := by ext; simp [Unique.uniq] - right_inv a := rfl /-- Fixing an element `b : β` gives an embedding `α ↪ α × β`. -/ @[simps] @@ -371,8 +370,6 @@ def subtypeInjectiveEquivEmbedding (α β : Sort*) : { f : α → β // Injective f } ≃ (α ↪ β) where toFun f := ⟨f.val, f.property⟩ invFun f := ⟨f, f.injective⟩ - left_inv _ := rfl - right_inv _ := rfl /-- If `α₁ ≃ α₂` and `β₁ ≃ β₂`, then the type of embeddings `α₁ ↪ β₁` is equivalent to the type of embeddings `α₂ ↪ β₂`. -/ diff --git a/Mathlib/Logic/Equiv/Basic.lean b/Mathlib/Logic/Equiv/Basic.lean index beb465860ddc95..a5b53587dc8388 100644 --- a/Mathlib/Logic/Equiv/Basic.lean +++ b/Mathlib/Logic/Equiv/Basic.lean @@ -42,7 +42,6 @@ def piOptionEquivProd {α} {β : Option α → Type*} : toFun f := (f none, fun a => f (some a)) invFun x a := Option.casesOn a x.fst x.snd left_inv f := funext fun a => by cases a <;> rfl - right_inv x := by simp section subtypeCongr @@ -414,7 +413,6 @@ def piEquivSubtypeSigma (ι) (π : ι → Type*) : (∀ i, π i) ≃ { f : ι → Σ i, π i // ∀ i, (f i).1 = i } where toFun := fun f => ⟨fun i => ⟨i, f i⟩, fun _ => rfl⟩ invFun := fun f i => by rw [← f.2 i]; exact (f.1 i).2 - left_inv := fun _ => funext fun _ => rfl right_inv := fun ⟨f, hf⟩ => Subtype.eq <| funext fun i => Sigma.eq (hf i).symm <| eq_of_heq <| rec_heq_of_heq _ <| by simp @@ -967,8 +965,6 @@ end (∀ i : {i // i ∈ s}, W i) ≃ (∀ i : {i // i ∈ t}, W i) where toFun f i := f ⟨i, h ▸ i.2⟩ invFun f i := f ⟨i, h.symm ▸ i.2⟩ - left_inv f := rfl - right_inv f := rfl lemma eq_conj {α α' β β' : Sort*} (ε₁ : α ≃ α') (ε₂ : β' ≃ β) (f : α → β) (f' : α' → β') : ε₂.symm ∘ f ∘ ε₁.symm = f' ↔ f = ε₂ ∘ f' ∘ ε₁ := by diff --git a/Mathlib/Logic/Equiv/Defs.lean b/Mathlib/Logic/Equiv/Defs.lean index e89ffc7eb45fb1..84a219a1b813c7 100644 --- a/Mathlib/Logic/Equiv/Defs.lean +++ b/Mathlib/Logic/Equiv/Defs.lean @@ -64,8 +64,8 @@ variable {α : Sort u} {β : Sort v} {γ : Sort w} structure Equiv (α : Sort*) (β : Sort _) where protected toFun : α → β protected invFun : β → α - protected left_inv : LeftInverse invFun toFun - protected right_inv : RightInverse invFun toFun + protected left_inv : LeftInverse invFun toFun := by intro; first | rfl | ext <;> rfl + protected right_inv : RightInverse invFun toFun := by intro; first | rfl | ext <;> rfl @[inherit_doc] infixl:25 " ≃ " => Equiv @@ -537,7 +537,6 @@ def piUnique [Unique α] (β : α → Sort*) : (∀ i, β i) ≃ β default wher toFun f := f default invFun := uniqueElim left_inv f := by ext i; cases Unique.eq_default i; rfl - right_inv _ := rfl /-- If `α` has a unique term, then the type of function `α → β` is equivalent to `β`. -/ @[simps! -fullyApplied apply symm_apply] @@ -554,7 +553,6 @@ def arrowPUnitOfIsEmpty (α β : Sort*) [IsEmpty α] : (α → β) ≃ PUnit.{u} toFun _ := PUnit.unit invFun _ := isEmptyElim left_inv _ := funext isEmptyElim - right_inv _ := rfl /-- The sort of maps from `Empty` is equivalent to `PUnit`. -/ def emptyArrowEquivPUnit (α : Sort*) : (Empty → α) ≃ PUnit.{u} := arrowPUnitOfIsEmpty _ _ @@ -574,16 +572,12 @@ section def psigmaEquivSigma {α} (β : α → Type*) : (Σ' i, β i) ≃ Σ i, β i where toFun a := ⟨a.1, a.2⟩ invFun a := ⟨a.1, a.2⟩ - left_inv _ := rfl - right_inv _ := rfl /-- A `PSigma`-type is equivalent to the corresponding `Sigma`-type. -/ @[simps apply symm_apply] def psigmaEquivSigmaPLift {α} (β : α → Sort*) : (Σ' i, β i) ≃ Σ i : PLift α, PLift (β i.down) where toFun a := ⟨PLift.up a.1, PLift.up a.2⟩ invFun a := ⟨a.1.down, a.2.down⟩ - left_inv _ := rfl - right_inv _ := rfl /-- A family of equivalences `Π a, β₁ a ≃ β₂ a` generates an equivalence between `Σ' a, β₁ a` and `Σ' a, β₂ a`. -/ @@ -631,8 +625,6 @@ theorem sigmaCongrRight_refl {α} {β : α → Type*} : def psigmaEquivSubtype {α : Type v} (P : α → Prop) : (Σ' i, P i) ≃ Subtype P where toFun x := ⟨x.1, x.2⟩ invFun x := ⟨x.1, x.2⟩ - left_inv _ := rfl - right_inv _ := rfl /-- A `Sigma` with `PLift` fibers is equivalent to the subtype. -/ def sigmaPLiftEquivSubtype {α : Type v} (P : α → Prop) : (Σ i, PLift (P i)) ≃ Subtype P := @@ -673,8 +665,6 @@ def functionSwap (α β : Sort*) (γ : α → β → Sort*) : ((a : α) → (b : β) → γ a b) ≃ ((b : β) → (a : α) → γ a b) where toFun := Function.swap invFun := Function.swap - left_inv _ := rfl - right_inv _ := rfl theorem _root_.Function.swap_bijective {α β : Sort*} {γ : α → β → Sort*} : Function.Bijective (@Function.swap _ _ γ) := @@ -713,16 +703,12 @@ def sigmaAssoc {α : Type*} {β : α → Type*} (γ : ∀ a : α, β a → Type* (Σ ab : Σ a : α, β a, γ ab.1 ab.2) ≃ Σ a : α, Σ b : β a, γ a b where toFun x := ⟨x.1.1, ⟨x.1.2, x.2⟩⟩ invFun x := ⟨⟨x.1, x.2.1⟩, x.2.2⟩ - left_inv _ := rfl - right_inv _ := rfl /-- The dependent product of sorts is associative up to an equivalence. -/ def pSigmaAssoc {α : Sort*} {β : α → Sort*} (γ : ∀ a : α, β a → Sort*) : (Σ' ab : Σ' a : α, β a, γ ab.1 ab.2) ≃ Σ' a : α, Σ' b : β a, γ a b where toFun x := ⟨x.1.1, ⟨x.1.2, x.2⟩⟩ invFun x := ⟨⟨x.1, x.2.1⟩, x.2.2⟩ - left_inv _ := rfl - right_inv _ := rfl end @@ -890,7 +876,6 @@ def sumIsLeft : {x : α ⊕ β // x.isLeft} ≃ α where toFun x := x.1.getLeft x.2 invFun a := ⟨.inl a, Sum.isLeft_inl⟩ left_inv | ⟨.inl _a, _⟩ => rfl - right_inv _a := rfl /-- The right summand of `α ⊕ β` is equivalent to `β`. -/ @[simps] @@ -898,6 +883,5 @@ def sumIsRight : {x : α ⊕ β // x.isRight} ≃ β where toFun x := x.1.getRight x.2 invFun b := ⟨.inr b, Sum.isRight_inr⟩ left_inv | ⟨.inr _b, _⟩ => rfl - right_inv _b := rfl end Equiv diff --git a/Mathlib/Logic/Equiv/Embedding.lean b/Mathlib/Logic/Equiv/Embedding.lean index 581266ce8d400b..1fd62eaf48c4b5 100644 --- a/Mathlib/Logic/Equiv/Embedding.lean +++ b/Mathlib/Logic/Equiv/Embedding.lean @@ -55,8 +55,6 @@ def codRestrict (α : Type*) {β : Type*} (bs : Set β) : (α ↪ bs) where toFun f := (f : α ↪ β).codRestrict bs f.prop invFun f := ⟨f.trans (Function.Embedding.subtype _), fun a => (f a).prop⟩ - left_inv x := by ext; rfl - right_inv x := by ext; rfl /-- Pairs of embeddings with disjoint ranges are equivalent to a dependent sum of embeddings, in which the second embedding cannot take values in the range of the first. -/ diff --git a/Mathlib/Logic/Equiv/Option.lean b/Mathlib/Logic/Equiv/Option.lean index 47bff14c0b178b..0e8a174bc9678a 100644 --- a/Mathlib/Logic/Equiv/Option.lean +++ b/Mathlib/Logic/Equiv/Option.lean @@ -42,13 +42,15 @@ theorem optionCongr_refl : optionCongr (Equiv.refl α) = Equiv.refl _ := ext <| congr_fun Option.map_id @[simp] -theorem optionCongr_symm (e : α ≃ β) : (optionCongr e).symm = optionCongr e.symm := +theorem optionCongr_symm (e : α ≃ β) : optionCongr e.symm = (optionCongr e).symm := rfl @[simp] theorem optionCongr_trans (e₁ : α ≃ β) (e₂ : β ≃ γ) : - (optionCongr e₁).trans (optionCongr e₂) = optionCongr (e₁.trans e₂) := - ext <| Option.map_map _ _ + optionCongr (e₁.trans e₂) = (optionCongr e₁).trans (optionCongr e₂) := by + ext x : 1 + symm + apply Option.map_map /-- When `α` and `β` are in the same universe, this is the same as the result of `EquivFunctor.mapEquiv`. -/ diff --git a/Mathlib/Logic/Equiv/Prod.lean b/Mathlib/Logic/Equiv/Prod.lean index d3efffd30fbaf5..cf31f63ab34ebc 100644 --- a/Mathlib/Logic/Equiv/Prod.lean +++ b/Mathlib/Logic/Equiv/Prod.lean @@ -36,8 +36,6 @@ namespace Equiv def pprodEquivProd {α β} : PProd α β ≃ α × β where toFun x := (x.1, x.2) invFun x := ⟨x.1, x.2⟩ - left_inv := fun _ => rfl - right_inv := fun _ => rfl /-- Product of two equivalences, in terms of `PProd`. If `α ≃ β` and `γ ≃ δ`, then `PProd α γ ≃ PProd β δ`. -/ @@ -104,8 +102,6 @@ def prodAssoc (α β γ) : (α × β) × γ ≃ α × β × γ := def prodProdProdComm (α β γ δ) : (α × β) × γ × δ ≃ (α × γ) × β × δ where toFun abcd := ((abcd.1.1, abcd.2.1), (abcd.1.2, abcd.2.2)) invFun acbd := ((acbd.1.1, acbd.2.1), (acbd.1.2, acbd.2.2)) - left_inv := fun ⟨⟨_a, _b⟩, ⟨_c, _d⟩⟩ => rfl - right_inv := fun ⟨⟨_a, _c⟩, ⟨_b, _d⟩⟩ => rfl @[simp] theorem prodProdProdComm_symm (α β γ δ) : @@ -367,8 +363,6 @@ def arrowProdEquivProdArrow (α : Type*) (β γ : α → Type*) : ((i : α) → β i × γ i) ≃ ((i : α) → β i) × ((i : α) → γ i) where toFun := fun f => (fun c => (f c).1, fun c => (f c).2) invFun := fun p c => (p.1 c, p.2 c) - left_inv := fun _ => rfl - right_inv := fun p => by cases p; rfl open Sum @@ -380,7 +374,6 @@ def sumPiEquivProdPi {ι ι'} (π : ι ⊕ ι' → Type*) : toFun f := ⟨fun i => f (inl i), fun i' => f (inr i')⟩ invFun g := Sum.rec g.1 g.2 left_inv f := by ext (i | i) <;> rfl - right_inv _ := Prod.ext rfl rfl /-- The equivalence between a product of two dependent functions types and a single dependent function type. Basically a symmetric version of `Equiv.sumPiEquivProdPi`. -/ @@ -466,7 +459,6 @@ def boolArrowEquivProd (α) : (Bool → α) ≃ α × α where toFun f := (f false, f true) invFun p b := b.casesOn p.1 p.2 left_inv _ := funext <| Bool.forall_bool.2 ⟨rfl, rfl⟩ - right_inv := fun _ => rfl end @@ -480,8 +472,6 @@ def subtypeProdEquivProd {α β} {p : α → Prop} {q : β → Prop} : { c : α × β // p c.1 ∧ q c.2 } ≃ { a // p a } × { b // q b } where toFun := fun x => ⟨⟨x.1.1, x.2.1⟩, ⟨x.1.2, x.2.2⟩⟩ invFun := fun x => ⟨⟨x.1.1, x.2.1⟩, ⟨x.1.2, x.2.2⟩⟩ - left_inv := fun ⟨⟨_, _⟩, ⟨_, _⟩⟩ => rfl - right_inv := fun ⟨⟨_, _⟩, ⟨_, _⟩⟩ => rfl /-- A subtype of a `Prod` that depends only on the first component is equivalent to the corresponding subtype of the first type times the second type. -/ @@ -489,16 +479,12 @@ def prodSubtypeFstEquivSubtypeProd {α β} {p : α → Prop} : {s : α × β // p s.1} ≃ {a // p a} × β where toFun x := ⟨⟨x.1.1, x.2⟩, x.1.2⟩ invFun x := ⟨⟨x.1.1, x.2⟩, x.1.2⟩ - left_inv _ := rfl - right_inv _ := rfl /-- A subtype of a `Prod` is equivalent to a sigma type whose fibers are subtypes. -/ def subtypeProdEquivSigmaSubtype {α β} (p : α → β → Prop) : { x : α × β // p x.1 x.2 } ≃ Σa, { b : β // p a b } where toFun x := ⟨x.1.1, x.1.2, x.property⟩ invFun x := ⟨⟨x.1, x.2⟩, x.2.property⟩ - left_inv x := by ext <;> rfl - right_inv := fun ⟨_, _, _⟩ => rfl /-- The type `∀ (i : α), β i` can be split as a product by separating the indices in `α` depending on whether they satisfy a predicate `p` or not. -/ diff --git a/Mathlib/Logic/Equiv/Set.lean b/Mathlib/Logic/Equiv/Set.lean index 82458ac34b2e9a..f67917a468bb71 100644 --- a/Mathlib/Logic/Equiv/Set.lean +++ b/Mathlib/Logic/Equiv/Set.lean @@ -153,8 +153,6 @@ def setProdEquivSigma {α β : Type*} (s : Set (α × β)) : s ≃ Σx : α, { y : β | (x, y) ∈ s } where toFun x := ⟨x.1.1, x.1.2, by simp⟩ invFun x := ⟨(x.1, x.2.1), x.2.2⟩ - left_inv := fun ⟨⟨_, _⟩, _⟩ => rfl - right_inv := fun ⟨_, _, _⟩ => rfl /-- The subtypes corresponding to equal sets are equivalent. -/ @[simps! apply symm_apply] @@ -409,12 +407,6 @@ protected def univPi {α : Type*} {β : α → Type*} (s : ∀ a, Set (β a)) : pi univ s ≃ ∀ a, s a where toFun f a := ⟨(f : ∀ a, β a) a, f.2 a (mem_univ a)⟩ invFun f := ⟨fun a => f a, fun a _ => (f a).2⟩ - left_inv := fun ⟨f, hf⟩ => by - ext a - rfl - right_inv f := by - ext a - rfl /-- If a function `f` is injective on a set `s`, then `s` is equivalent to `f '' s`. -/ protected noncomputable def imageOfInjOn {α β} (f : α → β) (s : Set α) (H : InjOn f s) : @@ -456,7 +448,7 @@ protected def powerset {α} (S : Set α) : 𝒫 S ≃ Set S where toFun := fun x : 𝒫 S => Subtype.val ⁻¹' (x : Set α) invFun := fun x : Set S => ⟨Subtype.val '' x, by rintro _ ⟨a : S, _, rfl⟩; exact a.2⟩ - left_inv x := by ext y;exact ⟨fun ⟨⟨_, _⟩, h, rfl⟩ => h, fun h => ⟨⟨_, x.2 h⟩, h, rfl⟩⟩ + left_inv x := by ext y; exact ⟨fun ⟨⟨_, _⟩, h, rfl⟩ => h, fun h => ⟨⟨_, x.2 h⟩, h, rfl⟩⟩ right_inv x := by ext; simp /-- If `s` is a set in `range f`, @@ -483,7 +475,6 @@ def rangeInl (α β : Type*) : Set.range (Sum.inl : α → α ⊕ β) ≃ α whe | ⟨.inr _, h⟩ => False.elim <| by rcases h with ⟨x, h'⟩; cases h' invFun x := ⟨.inl x, mem_range_self _⟩ left_inv := fun ⟨_, _, rfl⟩ => rfl - right_inv _ := rfl @[simp] lemma rangeInl_apply_inl {α : Type*} (β : Type*) (x : α) : (rangeInl α β) ⟨.inl x, mem_range_self _⟩ = x := @@ -497,7 +488,6 @@ def rangeInr (α β : Type*) : Set.range (Sum.inr : β → α ⊕ β) ≃ β whe | ⟨.inr x, _⟩ => x invFun x := ⟨.inr x, mem_range_self _⟩ left_inv := fun ⟨_, _, rfl⟩ => rfl - right_inv _ := rfl @[simp] lemma rangeInr_apply_inr (α : Type*) {β : Type*} (x : β) : (rangeInr α β) ⟨.inr x, mem_range_self _⟩ = x := diff --git a/Mathlib/Logic/Equiv/Sum.lean b/Mathlib/Logic/Equiv/Sum.lean index 166eea48011c9b..f12d038edd9c93 100644 --- a/Mathlib/Logic/Equiv/Sum.lean +++ b/Mathlib/Logic/Equiv/Sum.lean @@ -200,7 +200,6 @@ def sumEmpty (α β) [IsEmpty β] : α ⊕ β ≃ α where rcases s with (_ | x) · rfl · exact isEmptyElim x - right_inv _ := rfl @[simp] theorem sumEmpty_apply_inl {α β} [IsEmpty β] (a : α) : sumEmpty α β (Sum.inl a) = a := diff --git a/Mathlib/MeasureTheory/Constructions/Cylinders.lean b/Mathlib/MeasureTheory/Constructions/Cylinders.lean index e2261215ff3185..f327d0530e27ce 100644 --- a/Mathlib/MeasureTheory/Constructions/Cylinders.lean +++ b/Mathlib/MeasureTheory/Constructions/Cylinders.lean @@ -3,9 +3,11 @@ Copyright (c) 2023 Rémy Degenne. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Rémy Degenne, Peter Pfaffelhuber, Yaël Dillies, Kin Yau James Wong -/ +import Mathlib.Data.Finset.Lattice.Basic import Mathlib.MeasureTheory.MeasurableSpace.Constructions -import Mathlib.MeasureTheory.PiSystem +import Mathlib.MeasureTheory.SetSemiring import Mathlib.Topology.Constructions +import Mathlib.MeasureTheory.SetAlgebra /-! # π-systems of cylinders and square cylinders @@ -24,6 +26,8 @@ Given a finite set `s` of indices, a cylinder is the product of a set of `∀ i a product set. * `cylinder s S`: cylinder with base set `S : Set (∀ i : s, α i)` where `s` is a `Finset` +* `squareCylinder s S`: square cylinder with base set `S : (∀ i : s, Set (α i))` where + `s` is a `Finset` * `squareCylinders C` with `C : ∀ i, Set (Set (α i))`: set of all square cylinders such that for all `i` in the finset defining the box, the projection to `α i` belongs to `C i`. The main application of this is with `C i = {s : Set (α i) | MeasurableSet s}`. @@ -48,62 +52,105 @@ variable {ι : Type _} {α : ι → Type _} section squareCylinders -/-- Given a finite set `s` of indices, a square cylinder is the product of a set `S` of -`∀ i : s, α i` and of `univ` on the other indices. The set `S` is a product of sets `t i` such that +/-- Given a finite set `s` of indices, a square cylinder is the product of sets `t i : Set (α i)` +for `i ∈ s` and of `univ` on the other indices. -/ +def squareCylinder (s : Finset ι) (t : ∀ i, Set (α i)) : Set (∀ i, α i) := + (s : Set ι).pi t + +/-- The set `S` is a product of sets `t i` such that for all `i : s`, `t i ∈ C i`. `squareCylinders` is the set of all such squareCylinders. -/ def squareCylinders (C : ∀ i, Set (Set (α i))) : Set (Set (∀ i, α i)) := - {S | ∃ s : Finset ι, ∃ t ∈ univ.pi C, S = (s : Set ι).pi t} + {S | ∃ s : Finset ι, ∃ t ∈ univ.pi C, S = squareCylinder s t} theorem squareCylinders_eq_iUnion_image (C : ∀ i, Set (Set (α i))) : - squareCylinders C = ⋃ s : Finset ι, (fun t ↦ (s : Set ι).pi t) '' univ.pi C := by + squareCylinders C = ⋃ s : Finset ι, (s : Set ι).pi '' univ.pi C := by + ext1 f + simp only [squareCylinder, squareCylinders, mem_iUnion, mem_image, mem_univ_pi, exists_prop, + mem_setOf_eq, eq_comm (a := f)] + +theorem squareCylinders_eq_iUnion_image' (C : ∀ i, Set (Set (α i))) (hC : ∀ i, Nonempty (C i)) : + squareCylinders C = ⋃ s : Finset ι, (s : Set ι).pi '' (s : Set ι).pi C := by + classical ext1 f - simp only [squareCylinders, mem_iUnion, mem_image, mem_univ_pi, exists_prop, mem_setOf_eq, - eq_comm (a := f)] + simp only [squareCylinder, squareCylinders, mem_iUnion, mem_image, mem_setOf_eq, eq_comm (a := f)] + have h (s : Set ι): s.pi '' s.pi C = s.pi '' univ.pi C := by + refine pi_image_eq_of_subset hC (subset_univ s) + simp_rw [← mem_image, h] -theorem isPiSystem_squareCylinders {C : ∀ i, Set (Set (α i))} (hC : ∀ i, IsPiSystem (C i)) - (hC_univ : ∀ i, univ ∈ C i) : - IsPiSystem (squareCylinders C) := by - rintro S₁ ⟨s₁, t₁, h₁, rfl⟩ S₂ ⟨s₂, t₂, h₂, rfl⟩ hst_nonempty +@[simp] +theorem mem_squareCylinders (C : ∀ i, Set (Set (α i))) (hC : ∀ i, Nonempty (C i)) (S : _) : + S ∈ squareCylinders C ↔ ∃ (s t : _) (_ : ∀ i ∈ s, t i ∈ C i), S = squareCylinder s t := by + simp_rw [squareCylinders_eq_iUnion_image, squareCylinder] + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ + · simp only [mem_iUnion, mem_image, mem_pi, mem_univ, forall_const] at h + obtain ⟨s, t, h₀, h₁⟩ := h + use s, t + simp only [h₁, h₀, implies_true, exists_const] + · obtain ⟨s, t, h₀, rfl⟩ := h + simp only [mem_iUnion, mem_image, mem_pi, mem_univ, forall_const] + classical + use s, (fun i ↦ if i ∈ s.toSet then t i else (hC i).some) + refine ⟨fun i ↦ ?_ ,?_⟩ + · by_cases h : i ∈ s <;> simp only [Finset.mem_coe, h, ↓reduceIte, Subtype.coe_prop, h₀] + · refine Set.pi_congr rfl (fun i hi ↦ by simp only [hi, ↓reduceIte] at *) + +theorem squareCylinders_subset_pi (C : ∀ i, Set (Set (α i))) (hC : ∀ i, univ ∈ C i) : + squareCylinders C ⊆ univ.pi '' univ.pi C := by + intro S hS + obtain ⟨s, t, h₀, h₁⟩ := hS + simp only [squareCylinder, mem_pi, mem_univ, forall_const] at h₀ h₁ + classical + use fun i ↦ (if i ∈ s.toSet then (t i) else univ) + refine ⟨fun i ↦ ?_, ?_⟩ + · simp only [mem_pi, mem_univ, forall_const] + by_cases hi : i ∈ s.toSet <;> simp only [hi, ↓reduceIte] + · exact h₀ i + · exact hC i + · rw [h₁, univ_pi_ite s t] + +theorem isPiSystem_squareCylinders [∀ i, Inhabited (α i)] {C : ∀ i, Set (Set (α i))} + (hC : ∀ i, IsPiSystem (C i)) (hC_univ : ∀ i, univ ∈ C i) : IsPiSystem (squareCylinders C) := by classical + haveI h_nempty : ∀ i, Nonempty (C i) := fun i ↦ Nonempty.intro ⟨Set.univ, hC_univ i⟩ + rintro S₁ ⟨s₁, t₁, h₁, rfl⟩ S₂ ⟨s₂, t₂, h₂, rfl⟩ hst_nonempty let t₁' := s₁.piecewise t₁ (fun i ↦ univ) + simp only [Set.mem_pi, Set.mem_univ, forall_const] at h₁ h₂ + have ht₁ (i : ι) : t₁' i ∈ C i := by + by_cases h : i ∈ s₁ + · simp only [h, Finset.piecewise_eq_of_mem, t₁'] + exact h₁ i + · simp only [t₁'] + rw [Finset.piecewise_eq_of_notMem s₁ t₁ (fun i ↦ univ) h] + exact hC_univ i let t₂' := s₂.piecewise t₂ (fun i ↦ univ) - have h1 : ∀ i ∈ (s₁ : Set ι), t₁ i = t₁' i := - fun i hi ↦ (Finset.piecewise_eq_of_mem _ _ _ hi).symm - have h1' : ∀ i ∉ (s₁ : Set ι), t₁' i = univ := - fun i hi ↦ Finset.piecewise_eq_of_notMem _ _ _ hi - have h2 : ∀ i ∈ (s₂ : Set ι), t₂ i = t₂' i := - fun i hi ↦ (Finset.piecewise_eq_of_mem _ _ _ hi).symm - have h2' : ∀ i ∉ (s₂ : Set ι), t₂' i = univ := - fun i hi ↦ Finset.piecewise_eq_of_notMem _ _ _ hi - rw [Set.pi_congr rfl h1, Set.pi_congr rfl h2, ← union_pi_inter h1' h2'] - refine ⟨s₁ ∪ s₂, fun i ↦ t₁' i ∩ t₂' i, ?_, ?_⟩ - · rw [mem_univ_pi] - intro i - have : (t₁' i ∩ t₂' i).Nonempty := by - obtain ⟨f, hf⟩ := hst_nonempty - rw [Set.pi_congr rfl h1, Set.pi_congr rfl h2, mem_inter_iff, mem_pi, mem_pi] at hf - refine ⟨f i, ⟨?_, ?_⟩⟩ - · by_cases hi₁ : i ∈ s₁ - · exact hf.1 i hi₁ - · rw [h1' i hi₁] - exact mem_univ _ - · by_cases hi₂ : i ∈ s₂ - · exact hf.2 i hi₂ - · rw [h2' i hi₂] - exact mem_univ _ - refine hC i _ ?_ _ ?_ this - · by_cases hi₁ : i ∈ s₁ - · rw [← h1 i hi₁] - exact h₁ i (mem_univ _) - · rw [h1' i hi₁] - exact hC_univ i - · by_cases hi₂ : i ∈ s₂ - · rw [← h2 i hi₂] - exact h₂ i (mem_univ _) - · rw [h2' i hi₂] - exact hC_univ i - · rw [Finset.coe_union] + have ht₂ (i : ι) : t₂' i ∈ C i := by + by_cases h : i ∈ s₂ + · simp only [h, Finset.piecewise_eq_of_mem, t₂'] + exact h₂ i + · simp only [t₂'] + rw [Finset.piecewise_eq_of_notMem s₂ t₂ (fun i ↦ univ) h] + exact hC_univ i + have h₁ : (s₁ : Set ι).pi t₁' = (s₁ : Set ι).pi t₁ := by + refine Set.pi_congr rfl ?_ + exact fun i a ↦ (s₁.piecewise_eq_of_mem t₁ (fun i ↦ Set.univ) a) + have h₂ : (s₂ : Set ι).pi t₂' = (s₂ : Set ι).pi t₂ := by + refine Set.pi_congr rfl ?_ + exact fun i a ↦ (s₂.piecewise_eq_of_mem t₂ (fun i ↦ Set.univ) a) + have h : squareCylinder s₁ t₁ ∩ squareCylinder s₂ t₂ = squareCylinder (s₁ ∪ s₂) + (fun i ↦ t₁' i ∩ t₂' i) := by + rw [squareCylinder, squareCylinder, squareCylinder, Finset.coe_union, union_pi_inter, h₁, h₂] + <;> + exact fun i a ↦ Finset.piecewise_eq_of_notMem _ _ (fun i ↦ Set.univ) a + rw [h] at hst_nonempty ⊢ + rw [squareCylinder, squareCylinders_eq_iUnion_image' C, mem_iUnion] + · use (s₁ ∪ s₂), (fun i ↦ t₁' i ∩ t₂' i) + refine ⟨?_, rfl⟩ + apply fun i _ ↦ hC i (t₁' i) (ht₁ i) (t₂' i) (ht₂ i) _ + intro i hi + rw [squareCylinder, pi_nonempty_iff'] at hst_nonempty + exact hst_nonempty i hi + · assumption theorem comap_eval_le_generateFrom_squareCylinders_singleton (α : ι → Type*) [m : ∀ i, MeasurableSpace (α i)] (i : ι) : @@ -124,7 +171,7 @@ theorem comap_eval_le_generateFrom_squareCylinders_singleton · simp only [hji, not_false_iff, dif_neg, MeasurableSet.univ] · simp only [id_eq, eq_mpr_eq_cast, ← h] ext1 x - simp only [singleton_pi, Function.eval, cast_eq, dite_eq_ite, ite_true, mem_preimage] + simp only [singleton_pi, Function.eval, cast_eq, dite_eq_ite, ite_true, Set.mem_preimage] /-- The square cylinders formed from measurable sets generate the product σ-algebra. -/ theorem generateFrom_squareCylinders [∀ i, MeasurableSpace (α i)] : @@ -349,6 +396,33 @@ theorem diff_mem_measurableCylinders (hs : s ∈ measurableCylinders α) rw [diff_eq_compl_inter] exact inter_mem_measurableCylinders (compl_mem_measurableCylinders ht) hs + +section MeasurableCylinders + +lemma isSetAlgebra_measurableCylinders : IsSetAlgebra (measurableCylinders α) where + empty_mem := empty_mem_measurableCylinders α + compl_mem _ := compl_mem_measurableCylinders + union_mem _ _ := union_mem_measurableCylinders + +lemma isSetRing_measurableCylinders : IsSetRing (measurableCylinders α) := + isSetAlgebra_measurableCylinders.isSetRing + +lemma isSetSemiring_measurableCylinders : MeasureTheory.IsSetSemiring (measurableCylinders α) := + isSetRing_measurableCylinders.isSetSemiring + +end MeasurableCylinders + + +theorem iUnion_le_mem_measurableCylinders {s : ℕ → Set (∀ i : ι, α i)} + (hs : ∀ n, s n ∈ measurableCylinders α) (n : ℕ) : + (⋃ i ≤ n, s i) ∈ measurableCylinders α := + isSetRing_measurableCylinders.iUnion_le_mem hs n + +theorem iInter_le_mem_measurableCylinders {s : ℕ → Set (∀ i : ι, α i)} + (hs : ∀ n, s n ∈ measurableCylinders α) (n : ℕ) : + (⋂ i ≤ n, s i) ∈ measurableCylinders α := + isSetRing_measurableCylinders.iInter_le_mem hs n + /-- The measurable cylinders generate the product σ-algebra. -/ theorem generateFrom_measurableCylinders : MeasurableSpace.generateFrom (measurableCylinders α) = MeasurableSpace.pi := by @@ -457,4 +531,5 @@ lemma measurable_restrict_cylinderEvents (Δ : Set ι) : rw [@measurable_pi_iff]; exact fun i ↦ measurable_cylinderEvent_apply i.2 end cylinderEvents + end MeasureTheory diff --git a/Mathlib/MeasureTheory/Constructions/ProjectiveFamilyContent.lean b/Mathlib/MeasureTheory/Constructions/ProjectiveFamilyContent.lean index d198a0f5f15ae4..c424fbb7fb7855 100644 --- a/Mathlib/MeasureTheory/Constructions/ProjectiveFamilyContent.lean +++ b/Mathlib/MeasureTheory/Constructions/ProjectiveFamilyContent.lean @@ -5,7 +5,6 @@ Authors: Rémy Degenne, Peter Pfaffelhuber -/ import Mathlib.MeasureTheory.Constructions.Projective import Mathlib.MeasureTheory.Measure.AddContent -import Mathlib.MeasureTheory.SetAlgebra /-! # Additive content built from a projective family of measures @@ -43,21 +42,6 @@ variable {ι : Type*} {α : ι → Type*} {mα : ∀ i, MeasurableSpace (α i)} {P : ∀ J : Finset ι, Measure (Π j : J, α j)} {s t : Set (Π i, α i)} {I : Finset ι} {S : Set (Π i : I, α i)} -section MeasurableCylinders - -lemma isSetAlgebra_measurableCylinders : IsSetAlgebra (measurableCylinders α) where - empty_mem := empty_mem_measurableCylinders α - compl_mem _ := compl_mem_measurableCylinders - union_mem _ _ := union_mem_measurableCylinders - -lemma isSetRing_measurableCylinders : IsSetRing (measurableCylinders α) := - isSetAlgebra_measurableCylinders.isSetRing - -lemma isSetSemiring_measurableCylinders : MeasureTheory.IsSetSemiring (measurableCylinders α) := - isSetRing_measurableCylinders.isSetSemiring - -end MeasurableCylinders - section ProjectiveFamilyFun open Classical in diff --git a/Mathlib/MeasureTheory/Group/Convolution.lean b/Mathlib/MeasureTheory/Group/Convolution.lean index 77eb020dc5cebc..eb2d455b4be53b 100644 --- a/Mathlib/MeasureTheory/Group/Convolution.lean +++ b/Mathlib/MeasureTheory/Group/Convolution.lean @@ -37,30 +37,43 @@ scoped[MeasureTheory] infixr:80 " ∗ₘ " => MeasureTheory.Measure.mconv /-- Scoped notation for the additive convolution of measures. -/ scoped[MeasureTheory] infixr:80 " ∗ " => MeasureTheory.Measure.conv +@[to_additive] +theorem lintegral_mconv_eq_lintegral_prod [MeasurableMul₂ M] {μ ν : Measure M} + {f : M → ℝ≥0∞} (hf : Measurable f): + ∫⁻ z, f z ∂(μ ∗ₘ ν) = ∫⁻ z, f (z.1 * z.2) ∂(μ.prod ν) := by + rw [mconv, lintegral_map hf measurable_mul] + @[to_additive] theorem lintegral_mconv [MeasurableMul₂ M] {μ ν : Measure M} [SFinite ν] {f : M → ℝ≥0∞} (hf : Measurable f) : ∫⁻ z, f z ∂(μ ∗ₘ ν) = ∫⁻ x, ∫⁻ y, f (x * y) ∂ν ∂μ := by - rw [mconv, lintegral_map hf measurable_mul, lintegral_prod] - fun_prop + rw [lintegral_mconv_eq_lintegral_prod hf, lintegral_prod _ (by fun_prop)] + +@[to_additive] +lemma dirac_mconv [MeasurableMul₂ M] (x : M) (μ : Measure M) [SFinite μ] : + (Measure.dirac x) ∗ₘ μ = μ.map (fun y ↦ x * y) := by + unfold mconv + rw [Measure.dirac_prod, map_map (by fun_prop) (by fun_prop)] + simp [Function.comp_def] + +@[to_additive] +lemma mconv_dirac [MeasurableMul₂ M] (μ : Measure M) [SFinite μ] (x : M) : + μ ∗ₘ (Measure.dirac x) = μ.map (fun y ↦ y * x) := by + unfold mconv + rw [Measure.prod_dirac, map_map (by fun_prop) (by fun_prop)] + simp [Function.comp_def] /-- Convolution of the dirac measure at 1 with a measure μ returns μ. -/ @[to_additive (attr := simp) "Convolution of the dirac measure at 0 with a measure μ returns μ."] theorem dirac_one_mconv [MeasurableMul₂ M] (μ : Measure M) [SFinite μ] : (Measure.dirac 1) ∗ₘ μ = μ := by - unfold mconv - rw [MeasureTheory.Measure.dirac_prod, map_map (by fun_prop)] - · simp only [Function.comp_def, one_mul, map_id'] - fun_prop + simp [dirac_mconv] /-- Convolution of a measure μ with the dirac measure at 1 returns μ. -/ @[to_additive (attr := simp) "Convolution of a measure μ with the dirac measure at 0 returns μ."] theorem mconv_dirac_one [MeasurableMul₂ M] (μ : Measure M) [SFinite μ] : μ ∗ₘ (Measure.dirac 1) = μ := by - unfold mconv - rw [MeasureTheory.Measure.prod_dirac, map_map (by fun_prop)] - · simp only [Function.comp_def, mul_one, map_id'] - fun_prop + simp [mconv_dirac] /-- Convolution of the zero measure with a measure μ returns the zero measure. -/ @[to_additive (attr := simp) "Convolution of the zero measure with a measure μ returns diff --git a/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean b/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean index 472ea5e14b459a..2038826e1ab494 100644 --- a/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean +++ b/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean @@ -63,7 +63,7 @@ file `Mathlib/MeasureTheory/Integral/SetToL1.lean`). 4. (In the file `Mathlib/MeasureTheory/Integral/DominatedConvergence.lean`) `tendsto_integral_of_dominated_convergence` : the Lebesgue dominated convergence theorem -5. (In `Mathlib/MeasureTheory/Integral/SetIntegral.lean`) integration commutes with continuous +5. (In `Mathlib/MeasureTheory/Integral/Bochner/Set.lean`) integration commutes with continuous linear maps. * `ContinuousLinearMap.integral_comp_comm` @@ -120,7 +120,7 @@ Use `isClosed_property` or `DenseRange.induction_on` for this argument. * `∫ a, f a` : integral of `f` with respect to `volume`, the default measure on the ambient type We also define notations for integral on a set, which are described in the file -`Mathlib/MeasureTheory/Integral/SetIntegral.lean`. +`Mathlib/MeasureTheory/Integral/Bochner/Set.lean`. Note : `ₛ` is typed using `\_s`. Sometimes it shows as a box if the font is missing. diff --git a/Mathlib/MeasureTheory/Integral/Layercake.lean b/Mathlib/MeasureTheory/Integral/Layercake.lean index ea863ab3b3b7d5..92633c737b3c57 100644 --- a/Mathlib/MeasureTheory/Integral/Layercake.lean +++ b/Mathlib/MeasureTheory/Integral/Layercake.lean @@ -233,7 +233,7 @@ theorem lintegral_comp_eq_lintegral_meas_le_mul_of_measurable (μ : Measure α) apply setLIntegral_mono' measurableSet_Ioc (fun x hx ↦ ?_) rw [← h's] gcongr - exact fun a ha ↦ hx.2.trans (le_of_lt ha) + exact fun ha ↦ hx.2.trans (le_of_lt ha) _ ≤ ∫⁻ t in Ioi 0, μ {a : α | t ≤ f a} * ENNReal.ofReal (g t) := lintegral_mono_set Ioc_subset_Ioi_self /- The second integral is infinite, as one integrates among other things on those `ω` where diff --git a/Mathlib/MeasureTheory/Integral/Lebesgue/Basic.lean b/Mathlib/MeasureTheory/Integral/Lebesgue/Basic.lean index f473f27faa1434..e332a61d211bf0 100644 --- a/Mathlib/MeasureTheory/Integral/Lebesgue/Basic.lean +++ b/Mathlib/MeasureTheory/Integral/Lebesgue/Basic.lean @@ -392,7 +392,7 @@ theorem lintegral_add_measure (f : α → ℝ≥0∞) (μ ν : Measure α) : refine (ENNReal.iSup_add_iSup ?_).symm rintro ⟨φ, hφ⟩ ⟨ψ, hψ⟩ refine ⟨⟨φ ⊔ ψ, sup_le hφ hψ⟩, ?_⟩ - apply_rules [add_le_add, SimpleFunc.lintegral_mono, le_rfl] -- TODO: use `gcongr` + gcongr exacts [le_sup_left, le_sup_right] @[simp] diff --git a/Mathlib/MeasureTheory/Integral/Lebesgue/Markov.lean b/Mathlib/MeasureTheory/Integral/Lebesgue/Markov.lean index 24acdcae62979d..b23e71fca6d0cf 100644 --- a/Mathlib/MeasureTheory/Integral/Lebesgue/Markov.lean +++ b/Mathlib/MeasureTheory/Integral/Lebesgue/Markov.lean @@ -34,7 +34,7 @@ theorem lintegral_add_mul_meas_add_le_le_lintegral {f g : α → ℝ≥0∞} (hl rw [hφ_eq] _ ≤ ∫⁻ x, φ x ∂μ + ε * μ { x | φ x + ε ≤ g x } := by gcongr - exact fun x => (add_le_add_right (hφ_le _) _).trans + exact hφ_le _ _ = ∫⁻ x, φ x + indicator { x | φ x + ε ≤ g x } (fun _ => ε) x ∂μ := by rw [lintegral_add_left hφm, lintegral_indicator₀, setLIntegral_const] exact measurableSet_le (hφm.nullMeasurable.measurable'.add_const _) hg.nullMeasurable diff --git a/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/Basic.lean b/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/Basic.lean index 66ea08ca3ffd2a..26779ce7ba15c8 100644 --- a/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/Basic.lean +++ b/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/Basic.lean @@ -10,9 +10,10 @@ import Mathlib.Topology.PartitionOfUnity /-! # Riesz–Markov–Kakutani representation theorem -This file will prove the Riesz-Markov-Kakutani representation theorem on a locally compact -T2 space `X`. As a special case, the statements about linear functionals on bounded continuous -functions follows. +This file prepares technical definitions and results for the Riesz-Markov-Kakutani representation +theorem on a locally compact T2 space `X`. As a special case, the statements about linear +functionals on bounded continuous functions follows. Actual theorems, depending on the +linearity (`ℝ`, `ℝ≥0` or `ℂ`), are proven in separate files (`Real.lean`, `NNReal.lean`...) To make use of the existing API, the measure is constructed from a content `λ` on the compact subsets of a locally compact space X, rather than the usual construction of open sets in the diff --git a/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/NNReal.lean b/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/NNReal.lean new file mode 100644 index 00000000000000..2c4f1ff1b08a7a --- /dev/null +++ b/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/NNReal.lean @@ -0,0 +1,60 @@ +/- +Copyright (c) 2025 Yoh Tanimioto. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Yoh Tanimoto +-/ + +import Mathlib.MeasureTheory.Integral.RieszMarkovKakutani.Real + +/-! +# Riesz–Markov–Kakutani representation theorem for `ℝ≥0` + +This file proves the Riesz-Markov-Kakutani representation theorem on a locally compact +T2 space `X` for `ℝ≥0`-linear functionals `Λ`. + +## Implementation notes + +The proof depends on the version of the theorem for `ℝ`-linear functional Λ because in a standard +proof one has to prove the inequalities by `le_antisymm`, yet for `C_c(X, ℝ≥0)` there is no `Neg`. +Here we prove the result by writing `ℝ≥0`-linear `Λ` in terms of `ℝ`-linear `toRealLinear Λ` and by +reducing the statement to the `ℝ`-version of the theorem. + +## References + +* [Walter Rudin, Real and Complex Analysis.][Rud87] + +-/ + +open scoped NNReal + +open CompactlySupported CompactlySupportedContinuousMap MeasureTheory + +variable {X : Type*} [TopologicalSpace X] [T2Space X] [LocallyCompactSpace X] [MeasurableSpace X] + [BorelSpace X] +variable (Λ : C_c(X, ℝ≥0) →ₗ[ℝ≥0] ℝ≥0) + +namespace NNRealRMK + +/-- The **Riesz-Markov-Kakutani representation theorem**: given a positive linear functional `Λ`, +the (Bochner) integral of `f` (as a `ℝ`-valued function) with respect to the `rieszMeasure` +associated to `Λ` is equal to `Λ f`. -/ +theorem integral_rieszMeasure (f : C_c(X, ℝ≥0)) : ∫ (x : X), (f x : ℝ) ∂(rieszMeasure Λ) = Λ f := by + rw [← eq_toRealLinear_toReal Λ f, + ← RealRMK.integral_rieszMeasure (toRealLinear_nonneg Λ) f.toReal] + simp only [toReal_apply] + congr + exact Eq.symm (eq_toNNRealLinear_toRealLinear Λ) + +/-- The **Riesz-Markov-Kakutani representation theorem**: given a positive linear functional `Λ`, +the (lower) Lebesgue integral of `f` with respect to the `rieszMeasure` associated to `Λ` is equal +to `Λ f`. -/ +theorem lintegral_rieszMeasure (f : C_c(X, ℝ≥0)) : ∫⁻ (x : X), f x ∂(rieszMeasure Λ) = Λ f := by + rw [lintegral_coe_eq_integral, ← ENNReal.ofNNReal_toNNReal] + · rw [ENNReal.coe_inj, Real.toNNReal_of_nonneg (MeasureTheory.integral_nonneg (by intro a; simp)), + NNReal.eq_iff, NNReal.coe_mk] + exact integral_rieszMeasure Λ f + rw [rieszMeasure] + exact Continuous.integrable_of_hasCompactSupport (by fun_prop) + (HasCompactSupport.comp_left f.hasCompactSupport rfl) + +end NNRealRMK diff --git a/Mathlib/MeasureTheory/Measure/CharacteristicFunction.lean b/Mathlib/MeasureTheory/Measure/CharacteristicFunction.lean index 75890af1f911e7..b30977ab6d608c 100644 --- a/Mathlib/MeasureTheory/Measure/CharacteristicFunction.lean +++ b/Mathlib/MeasureTheory/Measure/CharacteristicFunction.lean @@ -196,7 +196,30 @@ lemma charFun_map_mul {μ : Measure ℝ} (r t : ℝ) : charFun (μ.map (r * ·)) t = charFun μ (r * t) := charFun_map_smul r t variable {E : Type*} [MeasurableSpace E] {μ ν : Measure E} {t : E} - [NormedAddCommGroup E] [InnerProductSpace ℝ E] [BorelSpace E] [SecondCountableTopology E] + [NormedAddCommGroup E] [InnerProductSpace ℝ E] + +@[simp] +lemma charFun_dirac [OpensMeasurableSpace E] {x : E} (t : E) : + charFun (Measure.dirac x) t = cexp (⟪x, t⟫ * I) := by + rw [charFun_apply, integral_dirac] + +lemma charFun_map_add_const [BorelSpace E] (r t : E) : + charFun (μ.map (· + r)) t = charFun μ t * cexp (⟪r, t⟫ * I) := by + rw [charFun_apply, charFun_apply, integral_map (by fun_prop) (by fun_prop), + ← integral_mul_const] + congr with a + rw [← Complex.exp_add] + congr + rw [inner_add_left] + simp only [ofReal_add] + ring + +lemma charFun_map_const_add [BorelSpace E] (r t : E) : + charFun (μ.map (r + ·)) t = charFun μ t * cexp (⟪r, t⟫ * I) := by + simp_rw [add_comm r] + exact charFun_map_add_const _ _ + +variable [BorelSpace E] [SecondCountableTopology E] /-- If the characteristic functions `charFun` of two finite measures `μ` and `ν` on a complete second-countable inner product space coincide, then `μ = ν`. -/ @@ -270,6 +293,21 @@ lemma charFunDual_dirac [OpensMeasurableSpace E] {x : E} (L : Dual ℝ E) : charFunDual (Measure.dirac x) L = cexp (L x * I) := by rw [charFunDual_apply, integral_dirac] +lemma charFunDual_map_add_const [BorelSpace E] (r : E) (L : Dual ℝ E) : + charFunDual (μ.map (· + r)) L = charFunDual μ L * cexp (L r * I) := by + rw [charFunDual_apply, charFunDual_apply, integral_map (by fun_prop) (by fun_prop), + ← integral_mul_const] + congr with a + rw [← Complex.exp_add] + congr + simp only [map_add, ofReal_add] + ring + +lemma charFunDual_map_const_add [BorelSpace E] (r : E) (L : Dual ℝ E) : + charFunDual (μ.map (r + ·)) L = charFunDual μ L * cexp (L r * I) := by + simp_rw [add_comm r] + exact charFunDual_map_add_const _ _ + /-- The characteristic function of a product of measures is a product of characteristic functions. -/ lemma charFunDual_prod [SFinite μ] [SFinite ν] (L : Dual ℝ (E × F)) : diff --git a/Mathlib/MeasureTheory/Measure/Dirac.lean b/Mathlib/MeasureTheory/Measure/Dirac.lean index 601633c6d26bea..2742eeba560060 100644 --- a/Mathlib/MeasureTheory/Measure/Dirac.lean +++ b/Mathlib/MeasureTheory/Measure/Dirac.lean @@ -179,6 +179,14 @@ instance Measure.dirac.isProbabilityMeasure {x : α} : IsProbabilityMeasure (dir instance Measure.dirac.instIsFiniteMeasure {a : α} : IsFiniteMeasure (dirac a) := inferInstance instance Measure.dirac.instSigmaFinite {a : α} : SigmaFinite (dirac a) := inferInstance +theorem dirac_eq_one_iff_mem (hs : MeasurableSet s) : dirac a s = 1 ↔ a ∈ s := by + rw [← prob_compl_eq_zero_iff hs, ← mem_ae_iff] + apply mem_ae_dirac_iff hs + +theorem dirac_eq_zero_iff_not_mem (hs : MeasurableSet s) : dirac a s = 0 ↔ a ∉ s := by + rw [← compl_compl s, ← mem_ae_iff, notMem_compl_iff] + apply mem_ae_dirac_iff (MeasurableSet.compl_iff.mpr hs) + theorem restrict_dirac' (hs : MeasurableSet s) [Decidable (a ∈ s)] : (Measure.dirac a).restrict s = if a ∈ s then Measure.dirac a else 0 := by split_ifs with has diff --git a/Mathlib/MeasureTheory/Measure/Haar/Basic.lean b/Mathlib/MeasureTheory/Measure/Haar/Basic.lean index a3dfeefbdc07b9..6398277348a599 100644 --- a/Mathlib/MeasureTheory/Measure/Haar/Basic.lean +++ b/Mathlib/MeasureTheory/Measure/Haar/Basic.lean @@ -193,11 +193,9 @@ theorem index_union_le (K₁ K₂ : Compacts G) {V : Set G} (hV : (interior V).N rcases index_elim K₁.2 hV with ⟨s, h1s, h2s⟩ rcases index_elim K₂.2 hV with ⟨t, h1t, h2t⟩ rw [← h2s, ← h2t] - refine le_trans ?_ (Finset.card_union_le _ _) - apply Nat.sInf_le; refine ⟨_, ?_, rfl⟩; rw [mem_setOf_eq] - apply union_subset <;> refine Subset.trans (by assumption) ?_ <;> - apply biUnion_subset_biUnion_left <;> intro g hg <;> simp only [mem_def] at hg <;> - simp only [mem_def, Multiset.mem_union, Finset.union_val, hg, or_true, true_or] + refine le_trans (Nat.sInf_le ⟨_, ?_, rfl⟩) (Finset.card_union_le _ _) + rw [mem_setOf_eq, Finset.set_biUnion_union] + gcongr @[to_additive addIndex_union_eq] theorem index_union_eq (K₁ K₂ : Compacts G) {V : Set G} (hV : (interior V).Nonempty) diff --git a/Mathlib/MeasureTheory/Measure/Haar/InnerProductSpace.lean b/Mathlib/MeasureTheory/Measure/Haar/InnerProductSpace.lean index 5646ce769f69eb..b36f486f669509 100644 --- a/Mathlib/MeasureTheory/Measure/Haar/InnerProductSpace.lean +++ b/Mathlib/MeasureTheory/Measure/Haar/InnerProductSpace.lean @@ -40,7 +40,9 @@ def toMeasurableEquiv : E ≃ᵐ F where @[deprecated (since := "2025-03-22")] alias coe_toMeasureEquiv := coe_toMeasurableEquiv -theorem toMeasurableEquiv_symm : f.toMeasurableEquiv.symm = f.symm.toMeasurableEquiv := rfl +@[simp] theorem toMeasurableEquiv_symm : f.symm.toMeasurableEquiv = f.toMeasurableEquiv.symm := rfl + +@[simp] lemma coe_symm_toMeasurableEquiv : ⇑f.toMeasurableEquiv.symm = f.symm := rfl @[deprecated (since := "2025-03-22")] alias toMeasureEquiv_symm := toMeasurableEquiv_symm diff --git a/Mathlib/MeasureTheory/Measure/Lebesgue/Complex.lean b/Mathlib/MeasureTheory/Measure/Lebesgue/Complex.lean index 694263be461597..7bc8673533340e 100644 --- a/Mathlib/MeasureTheory/Measure/Lebesgue/Complex.lean +++ b/Mathlib/MeasureTheory/Measure/Lebesgue/Complex.lean @@ -50,8 +50,7 @@ theorem volume_preserving_equiv_pi : MeasurePreserving measurableEquivPi := by convert (measurableEquivPi.symm.measurable.measurePreserving volume).symm rw [← addHaarMeasure_eq_volume_pi, ← Basis.parallelepiped_basisFun, ← Basis.addHaar, measurableEquivPi, Homeomorph.toMeasurableEquiv_symm_coe, - ContinuousLinearEquiv.symm_toHomeomorph, ContinuousLinearEquiv.coe_toHomeomorph, - Basis.map_addHaar, eq_comm] + ContinuousLinearEquiv.coe_symm_toHomeomorph, Basis.map_addHaar, eq_comm] exact (Basis.addHaar_eq_iff _ _).mpr Complex.orthonormalBasisOneI.volume_parallelepiped theorem volume_preserving_equiv_real_prod : MeasurePreserving measurableEquivRealProd := diff --git a/Mathlib/MeasureTheory/Measure/Lebesgue/VolumeOfBalls.lean b/Mathlib/MeasureTheory/Measure/Lebesgue/VolumeOfBalls.lean index 28efaacb5cfd05..eb70914ed918bc 100644 --- a/Mathlib/MeasureTheory/Measure/Lebesgue/VolumeOfBalls.lean +++ b/Mathlib/MeasureTheory/Measure/Lebesgue/VolumeOfBalls.lean @@ -331,9 +331,9 @@ theorem volume_ball (x : EuclideanSpace ℝ ι) (r : ℝ) : rw [Measure.addHaar_ball _ _ hr, this, ofReal_pow hr, finrank_euclideanSpace] rw [← ((volume_preserving_measurableEquiv _).symm).measure_preimage measurableSet_ball.nullMeasurableSet] - convert (volume_sum_rpow_lt_one ι one_le_two) using 4 - · simp_rw [ball_zero_eq _ zero_le_one, one_pow, Real.rpow_two, sq_abs, - Set.setOf_app_iff] + simp only [Set.preimage, ball_zero_eq _ zero_le_one, one_pow, Set.mem_setOf_eq] + convert volume_sum_rpow_lt_one ι one_le_two using 4 + · simp [one_pow, Real.rpow_two, sq_abs, EuclideanSpace.measurableEquiv] · rw [Gamma_add_one (by norm_num), Gamma_one_half_eq, ← mul_assoc, mul_div_cancel₀ _ two_ne_zero, one_mul] diff --git a/Mathlib/MeasureTheory/Measure/WithDensity.lean b/Mathlib/MeasureTheory/Measure/WithDensity.lean index 15f2d908441c8e..bca57e34e78f59 100644 --- a/Mathlib/MeasureTheory/Measure/WithDensity.lean +++ b/Mathlib/MeasureTheory/Measure/WithDensity.lean @@ -5,7 +5,8 @@ Authors: Mario Carneiro, Johannes Hölzl -/ import Mathlib.MeasureTheory.Integral.Lebesgue.Countable import Mathlib.MeasureTheory.Measure.Decomposition.Exhaustion -import Mathlib.MeasureTheory.Measure.Prod +import Mathlib.MeasureTheory.Group.Convolution +import Mathlib.Analysis.LConvolution /-! # Measure with a given density with respect to another measure @@ -701,4 +702,31 @@ lemma IsLocallyFiniteMeasure.withDensity_ofReal {f : α → ℝ} (hf : Continuou IsLocallyFiniteMeasure (μ.withDensity fun x ↦ .ofReal (f x)) := .withDensity_coe <| continuous_real_toNNReal.comp hf +section Conv + +variable {G : Type*} [Group G] [MeasureSpace G] [MeasurableMul₂ G] [MeasurableInv G] + {μ : Measure G} [SFinite μ] [IsMulLeftInvariant μ] + +@[to_additive] +theorem mconv_withDensity_eq_mlconvolution₀ {f g : G → ℝ≥0∞} + (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : + μ.withDensity f ∗ₘ μ.withDensity g = μ.withDensity (f ⋆ₘₗ[μ] g) := by + refine ext_of_lintegral _ fun φ hφ ↦ ?_ + rw [lintegral_mconv_eq_lintegral_prod hφ, prod_withDensity₀ hf hg, + lintegral_withDensity_eq_lintegral_mul₀, + lintegral_withDensity_eq_lintegral_mul₀, lintegral_prod, + lintegral_congr (fun x ↦ by apply (lintegral_mul_left_eq_self _ x⁻¹).symm), + lintegral_lintegral_swap] + · simp only [Pi.mul_apply, mul_inv_cancel_left, mlconvolution_def] + conv in (∫⁻ _ , _ ∂μ) * φ _ => rw[(lintegral_mul_const'' _ (by fun_prop)).symm] + all_goals first | fun_prop | simp; fun_prop + +@[to_additive] +theorem mconv_withDensity_eq_mlconvolution {f g : G → ℝ≥0∞} + (hf : Measurable f) (hg : Measurable g) : + μ.withDensity f ∗ₘ μ.withDensity g = μ.withDensity (f ⋆ₘₗ[μ] g) := + mconv_withDensity_eq_mlconvolution₀ hf.aemeasurable hg.aemeasurable + +end Conv + end MeasureTheory diff --git a/Mathlib/MeasureTheory/PiSystem.lean b/Mathlib/MeasureTheory/PiSystem.lean index cb23303e80d3ad..c6bc04867088c5 100644 --- a/Mathlib/MeasureTheory/PiSystem.lean +++ b/Mathlib/MeasureTheory/PiSystem.lean @@ -97,6 +97,14 @@ theorem IsPiSystem.insert_univ {S : Set (Set α)} (h_pi : IsPiSystem S) : · simp [hs, ht] · exact Set.mem_insert_of_mem _ (h_pi s hs t ht hst) +lemma IsPiSystem.iff_of_empty_mem (S : Set (Set α)) (hS : ∅ ∈ S) : + (IsPiSystem S) ↔ (∀ s ∈ S, ∀ t ∈ S, s ∩ t ∈ S) := by + refine ⟨fun h s hs t ht ↦ ?_, fun h s hs t ht _ ↦ h s hs t ht⟩ + by_cases h' : (s ∩ t).Nonempty + · exact h s hs t ht h' + · push_neg at h' + exact h' ▸ hS + theorem IsPiSystem.comap {α β} {S : Set (Set β)} (h_pi : IsPiSystem S) (f : α → β) : IsPiSystem { s : Set α | ∃ t ∈ S, f ⁻¹' t = s } := by rintro _ ⟨s, hs_mem, rfl⟩ _ ⟨t, ht_mem, rfl⟩ hst diff --git a/Mathlib/ModelTheory/Algebra/Field/IsAlgClosed.lean b/Mathlib/ModelTheory/Algebra/Field/IsAlgClosed.lean index 16ae2655b19e8b..c127c5d5dd706d 100644 --- a/Mathlib/ModelTheory/Algebra/Field/IsAlgClosed.lean +++ b/Mathlib/ModelTheory/Algebra/Field/IsAlgClosed.lean @@ -62,10 +62,7 @@ theorem lift_genericMonicPoly [CommRing K] [Nontrivial K] {n : ℕ} (v : Fin (n FreeCommRing.lift v (genericMonicPoly n) = (((monicEquivDegreeLT n).trans (degreeLTEquiv K n).toEquiv).symm (v ∘ Fin.castSucc)).1.eval (v (Fin.last _)) := by - simp only [genericMonicPoly, map_add, map_pow, lift_of, map_sum, map_mul, monicEquivDegreeLT, - degreeLTEquiv, Equiv.symm_trans_apply, LinearEquiv.coe_toEquiv_symm, EquivLike.coe_coe, - LinearEquiv.coe_symm_mk, Function.comp_apply, Equiv.coe_fn_symm_mk, eval_add, eval_pow, eval_X, - eval_finset_sum, eval_monomial] + simp [genericMonicPoly, monicEquivDegreeLT, degreeLTEquiv, eval_finset_sum] /-- A sentence saying every monic polynomial of degree `n` has a root. -/ noncomputable def genericMonicPolyHasRoot (n : ℕ) : Language.ring.Sentence := diff --git a/Mathlib/ModelTheory/Algebra/Ring/Basic.lean b/Mathlib/ModelTheory/Algebra/Ring/Basic.lean index 7f00b26ba319fa..d1979e875ab990 100644 --- a/Mathlib/ModelTheory/Algebra/Ring/Basic.lean +++ b/Mathlib/ModelTheory/Algebra/Ring/Basic.lean @@ -247,9 +247,7 @@ def languageEquivEquivRingEquiv {R S : Type*} { f with map_fun' := fun {n} f => by cases f <;> simp - map_rel' := fun {n} f => by cases f }, - left_inv f := rfl - right_inv f := rfl } + map_rel' := fun {n} f => by cases f } } variable (R : Type*) [Language.ring.Structure R] diff --git a/Mathlib/ModelTheory/Semantics.lean b/Mathlib/ModelTheory/Semantics.lean index 8f30232edfd30f..69042f9829d8f1 100644 --- a/Mathlib/ModelTheory/Semantics.lean +++ b/Mathlib/ModelTheory/Semantics.lean @@ -78,6 +78,11 @@ theorem realize_var (v : α → M) (k) : realize v (var k : L.Term α) = v k := theorem realize_func (v : α → M) {n} (f : L.Functions n) (ts) : realize v (func f ts : L.Term α) = funMap f fun i => (ts i).realize v := rfl +@[simp] +theorem realize_function_term {n} (v : Fin n → M) (f : L.Functions n) : + f.term.realize v = funMap f v := by + rfl + @[simp] theorem realize_relabel {t : L.Term α} {g : α → β} {v : β → M} : (t.relabel g).realize v = t.realize (v ∘ g) := by @@ -268,6 +273,17 @@ theorem realize_foldr_inf (l : List (L.BoundedFormula α n)) (v : α → M) (xs theorem realize_imp : (φ.imp ψ).Realize v xs ↔ φ.Realize v xs → ψ.Realize v xs := by simp only [Realize] +/-- List.foldr on BoundedFormula.imp gives a big "And" of input conditions. -/ +theorem realize_foldr_imp {k : ℕ} (l : List (L.BoundedFormula α k)) + (f : L.BoundedFormula α k) : + ∀ (v : α → M) xs, + (l.foldr BoundedFormula.imp f).Realize v xs = + ((∀ i ∈ l, i.Realize v xs) → f.Realize v xs) := by + intro v xs + induction l + next => simp + next f' _ _ => by_cases f'.Realize v xs <;> simp [*] + @[simp] theorem realize_rel {k : ℕ} {R : L.Relations k} {ts : Fin k → L.Term _} : (R.boundedFormula ts).Realize v xs ↔ RelMap R fun i => (ts i).realize (Sum.elim v xs) := @@ -854,6 +870,15 @@ theorem realize_iInf [Finite β] {f : β → L.BoundedFormula α n} forall_exists_index, forall_apply_eq_imp_iff] @[simp] +theorem _root_.FirstOrder.Language.Formula.realize_iSup [Finite β] {f : β → L.Formula α} + {v : α → M} : (Formula.iSup f).Realize v ↔ ∃ b, (f b).Realize v := by + simp [Formula.iSup, Formula.Realize] + +@[simp] +theorem _root_.FirstOrder.Language.Formula.realize_iInf [Finite β] {f : β → L.Formula α} + {v : α → M} : (Formula.iInf f).Realize v ↔ ∀ b, (f b).Realize v := by + simp [Formula.iInf, Formula.Realize] + theorem _root_.FirstOrder.Language.Formula.realize_iExsUnique [Finite γ] {φ : L.Formula (α ⊕ γ)} {v : α → M} : (φ.iExsUnique γ).Realize v ↔ ∃! (i : γ → M), φ.Realize (Sum.elim v i) := by diff --git a/Mathlib/ModelTheory/Substructures.lean b/Mathlib/ModelTheory/Substructures.lean index 323ce5442955f8..ab81b301c8b860 100644 --- a/Mathlib/ModelTheory/Substructures.lean +++ b/Mathlib/ModelTheory/Substructures.lean @@ -649,7 +649,6 @@ def topEquiv : (⊤ : L.Substructure M) ≃[L] M where toFun := subtype ⊤ invFun m := ⟨m, mem_top m⟩ left_inv m := by simp - right_inv _ := rfl @[simp] theorem coe_topEquiv : diff --git a/Mathlib/ModelTheory/Syntax.lean b/Mathlib/ModelTheory/Syntax.lean index 44539c8db2140d..7decf352e809d2 100644 --- a/Mathlib/ModelTheory/Syntax.lean +++ b/Mathlib/ModelTheory/Syntax.lean @@ -31,6 +31,7 @@ This file defines first-order terms, formulas, sentences, and theories in a styl above a particular index. - `FirstOrder.Language.Term.subst` and `FirstOrder.Language.BoundedFormula.subst` substitute variables with given terms. +- `FirstOrder.Language.Term.substFunc` instead substitutes function definitions with given terms. - Language maps can act on syntactic objects with functions such as `FirstOrder.Language.LHom.onFormula`. - `FirstOrder.Language.Term.constantsVarsEquiv` and @@ -169,6 +170,10 @@ def Functions.apply₁ (f : L.Functions 1) (t : L.Term α) : L.Term α := def Functions.apply₂ (f : L.Functions 2) (t₁ t₂ : L.Term α) : L.Term α := func f ![t₁, t₂] +/-- The representation of a function symbol as a term, on fresh variables indexed by Fin. -/ +def Functions.term {n : ℕ} (f : L.Functions n) : L.Term (Fin n) := + func f Term.var + namespace Term /-- Sends a term with constants to a term with extra variables. -/ @@ -237,6 +242,18 @@ def subst : L.Term α → (α → L.Term β) → L.Term β | var a, tf => tf a | func f ts, tf => func f fun i => (ts i).subst tf +/-- Substitutes the functions in a given term with expressions. -/ +@[simp] +def substFunc : L.Term α → (∀ {n : ℕ}, L.Functions n → L'.Term (Fin n)) → L'.Term α + | var a, _ => var a + | func f ts, tf => (tf f).subst fun i ↦ (ts i).substFunc tf + +@[simp] +theorem substFunc_term (t : L.Term α) : t.substFunc Functions.term = t := by + induction t + · rfl + · simp only [substFunc, Functions.term, subst, ‹∀_, _›] + end Term /-- `&n` is notation for the `n`-th free variable of a bounded formula. -/ @@ -598,12 +615,18 @@ def toFormula : ∀ {n : ℕ}, L.BoundedFormula α n → L.Formula (α ⊕ (Fin (φ.toFormula.relabel (Sum.elim (Sum.inl ∘ Sum.inl) (Sum.map Sum.inr id ∘ finSumFinEquiv.symm))).all -/-- Take the disjunction of a finite set of formulas -/ +/-- Take the disjunction of a finite set of formulas. + +Note that this is an arbitrary formula defined using the axiom of choice. It is only well-defined up +to equivalence of formulas. -/ noncomputable def iSup [Finite β] (f : β → L.BoundedFormula α n) : L.BoundedFormula α n := let _ := Fintype.ofFinite β ((Finset.univ : Finset β).toList.map f).foldr (· ⊔ ·) ⊥ -/-- Take the conjunction of a finite set of formulas -/ +/-- Take the conjunction of a finite set of formulas. + +Note that this is an arbitrary formula defined using the axiom of choice. It is only well-defined up +to equivalence of formulas. -/ noncomputable def iInf [Finite β] (f : β → L.BoundedFormula α n) : L.BoundedFormula α n := let _ := Fintype.ofFinite β ((Finset.univ : Finset β).toList.map f).foldr (· ⊓ ·) ⊤ @@ -767,6 +790,20 @@ noncomputable def iExsUnique [Finite β] (φ : L.Formula (α ⊕ β)) : L.Formul protected nonrec abbrev iff (φ ψ : L.Formula α) : L.Formula α := φ.iff ψ +/-- Take the disjunction of finitely many formulas. + +Note that this is an arbitrary formula defined using the axiom of choice. It is only well-defined up +to equivalence of formulas. -/ +noncomputable def iSup [Finite α] (f : α → L.Formula β) : L.Formula β := + BoundedFormula.iSup f + +/-- Take the conjunction of finitely many formulas. + +Note that this is an arbitrary formula defined using the axiom of choice. It is only well-defined up +to equivalence of formulas. -/ +noncomputable def iInf [Finite α] (f : α → L.Formula β) : L.Formula β := + BoundedFormula.iInf f + /-- A bijection sending formulas to sentences with constants. -/ def equivSentence : L.Formula α ≃ L[[α]].Sentence := (BoundedFormula.constantsVarsEquiv.trans (BoundedFormula.relabelEquiv (Equiv.sumEmpty _ _))).symm diff --git a/Mathlib/NumberTheory/ClassNumber/Finite.lean b/Mathlib/NumberTheory/ClassNumber/Finite.lean index 28adee7e5db9dc..82aa3c115dce4f 100644 --- a/Mathlib/NumberTheory/ClassNumber/Finite.lean +++ b/Mathlib/NumberTheory/ClassNumber/Finite.lean @@ -297,7 +297,7 @@ theorem exists_mk0_eq_mk0 [IsDedekindDomain S] [Algebra.IsAlgebraic R S] (I : (I simp only [Algebra.smul_def] at lt rw [← sub_eq_zero.mp (b_min _ (I.1.sub_mem (I.1.mul_mem_left _ ha) (I.1.mul_mem_left _ b_mem)) lt)] - refine mul_dvd_mul_right (dvd_trans (RingHom.map_dvd _ ?_) hr') _ + refine mul_dvd_mul_right (dvd_trans (map_dvd _ ?_) hr') _ exact Multiset.dvd_prod (Multiset.mem_map.mpr ⟨_, r_mem, rfl⟩) /-- `ClassGroup.mkMMem` is a specialization of `ClassGroup.mk0` to (the finite set of) diff --git a/Mathlib/NumberTheory/Cyclotomic/PrimitiveRoots.lean b/Mathlib/NumberTheory/Cyclotomic/PrimitiveRoots.lean index 27d58b0b603b1b..e7a567df2044be 100644 --- a/Mathlib/NumberTheory/Cyclotomic/PrimitiveRoots.lean +++ b/Mathlib/NumberTheory/Cyclotomic/PrimitiveRoots.lean @@ -154,9 +154,7 @@ noncomputable def embeddingsEquivPrimitiveRoots (C : Type*) [CommRing C] [IsDoma cases x rwa [aeval_def, eval₂_eq_eval_map, hζ.powerBasis_gen K, ← hζ.minpoly_eq_cyclotomic_of_irreducible hirr, map_cyclotomic, ← IsRoot.def, - isRoot_cyclotomic_iff, ← mem_primitiveRoots (NeZero.pos _)] - left_inv := fun _ => Subtype.ext rfl - right_inv := fun _ => Subtype.ext rfl } + isRoot_cyclotomic_iff, ← mem_primitiveRoots (NeZero.pos _)] } -- Porting note: renamed argument `φ`: "expected '_' or identifier" @[simp] diff --git a/Mathlib/NumberTheory/KummerDedekind.lean b/Mathlib/NumberTheory/KummerDedekind.lean index 880cb4327601b4..b4d8f57837eb30 100644 --- a/Mathlib/NumberTheory/KummerDedekind.lean +++ b/Mathlib/NumberTheory/KummerDedekind.lean @@ -19,17 +19,17 @@ with a formula). ## Main definitions -* `normalizedFactorsMapEquivNormalizedFactorsMinPolyMk` : The bijection in the - Kummer-Dedekind theorem. This is the pairing between the prime factors of `I * S` and the prime - factors of `f mod I`. +* `normalizedFactorsMapEquivNormalizedFactorsMinPolyMk` : The bijection in the Kummer-Dedekind + theorem. This is the pairing between the prime factors of `I * S` and the prime factors of + `f mod I`. ## Main results * `normalized_factors_ideal_map_eq_normalized_factors_min_poly_mk_map` : The Kummer-Dedekind - theorem. + theorem. * `Ideal.irreducible_map_of_irreducible_minpoly` : `I.map (algebraMap R S)` is irreducible if - `(map (Ideal.Quotient.mk I) (minpoly R pb.gen))` is irreducible, where `pb` is a power basis - of `S` over `R`. + `(map (Ideal.Quotient.mk I) (minpoly R pb.gen))` is irreducible, where `pb` is a power basis + of `S` over `R`. * `normalizedFactorsMapEquivNormalizedFactorsMinPolyMk_symm_apply_eq_span` : Let `Q` be a lift of factor of the minimal polynomial of `x`, a generator of `S` over `R`, taken `mod I`. Then (the reduction of) `Q` corresponds via diff --git a/Mathlib/NumberTheory/LSeries/Basic.lean b/Mathlib/NumberTheory/LSeries/Basic.lean index 81eb23ecc52024..1e508be3293a34 100644 --- a/Mathlib/NumberTheory/LSeries/Basic.lean +++ b/Mathlib/NumberTheory/LSeries/Basic.lean @@ -15,24 +15,23 @@ Given a sequence `f: ℕ → ℂ`, we define the corresponding L-series. ## Main Definitions * `LSeries.term f s n` is the `n`th term of the L-series of the sequence `f` at `s : ℂ`. - We define it to be zero when `n = 0`. + We define it to be zero when `n = 0`. -* `LSeries f` is the L-series with a given sequence `f` as its - coefficients. This is not the analytic continuation (which does not necessarily exist), - just the sum of the infinite series if it exists and zero otherwise. +* `LSeries f` is the L-series with a given sequence `f` as its coefficients. This is not the + analytic continuation (which does not necessarily exist), just the sum of the infinite series if + it exists and zero otherwise. * `LSeriesSummable f s` indicates that the L-series of `f` converges at `s : ℂ`. -* `LSeriesHasSum f s a` expresses that the L-series of `f` converges (absolutely) - at `s : ℂ` to `a : ℂ`. +* `LSeriesHasSum f s a` expresses that the L-series of `f` converges (absolutely) at `s : ℂ` to + `a : ℂ`. ## Main Results -* `LSeriesSummable_of_isBigO_rpow`: the `LSeries` of a sequence `f` such that - `f = O(n^(x-1))` converges at `s` when `x < s.re`. +* `LSeriesSummable_of_isBigO_rpow`: the `LSeries` of a sequence `f` such that `f = O(n^(x-1))` + converges at `s` when `x < s.re`. -* `LSeriesSummable.isBigO_rpow`: if the `LSeries` of `f` is summable at `s`, - then `f = O(n^(re s))`. +* `LSeriesSummable.isBigO_rpow`: if the `LSeries` of `f` is summable at `s`, then `f = O(n^(re s))`. ## Notation diff --git a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/IsBoundedAtImInfty.lean b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/IsBoundedAtImInfty.lean index cc51441e783cc2..48dc6040d103a9 100644 --- a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/IsBoundedAtImInfty.lean +++ b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/IsBoundedAtImInfty.lean @@ -5,8 +5,8 @@ Authors: Chris Birkbeck -/ import Mathlib.Analysis.Complex.UpperHalfPlane.FunctionsBoundedAtInfty -import Mathlib.Analysis.Normed.Order.Lattice -import Mathlib.NumberTheory.ModularForms.EisensteinSeries.UniformConvergence +import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Defs +import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Summable import Mathlib.NumberTheory.ModularForms.Identities /-! @@ -28,6 +28,7 @@ we then use our bounds for Eisenstein series in these vertical strips to get the noncomputable section open ModularForm UpperHalfPlane Matrix SlashInvariantForm CongruenceSubgroup + open scoped MatrixGroups namespace EisensteinSeries diff --git a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/Summable.lean b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/Summable.lean new file mode 100644 index 00000000000000..45cf8fb3251c69 --- /dev/null +++ b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/Summable.lean @@ -0,0 +1,154 @@ +/- +Copyright (c) 2024 Chris Birkbeck. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Birkbeck +-/ + +import Mathlib.Analysis.Complex.UpperHalfPlane.Topology +import Mathlib.Analysis.PSeries +import Mathlib.Order.Interval.Finset.Box +import Mathlib.Analysis.Asymptotics.Defs + +/-! +# Summability of Eisenstein series + +We gather results about the summability of Eisenstein series, particularly +the summability of the Eisenstein series summands, which are used in the proof of the +boundedness of Eisenstein series at infinity. +-/ + +noncomputable section + +open Complex UpperHalfPlane Set Finset Topology Filter Asymptotics + +open scoped UpperHalfPlane Topology BigOperators Nat + +variable (z : ℍ) + +namespace EisensteinSeries + +lemma norm_eq_max_natAbs (x : Fin 2 → ℤ) : ‖x‖ = max (x 0).natAbs (x 1).natAbs := by + rw [← coe_nnnorm, ← NNReal.coe_natCast, NNReal.coe_inj, Nat.cast_max] + refine eq_of_forall_ge_iff fun c ↦ ?_ + simp only [pi_nnnorm_le_iff, Fin.forall_fin_two, max_le_iff, NNReal.natCast_natAbs] + +section bounding_functions + +/-- Auxiliary function used for bounding Eisenstein series, defined as + `z.im ^ 2 / (z.re ^ 2 + z.im ^ 2)`. -/ +def r1 : ℝ := z.im ^ 2 / (z.re ^ 2 + z.im ^ 2) + +lemma r1_eq : r1 z = 1 / ((z.re / z.im) ^ 2 + 1) := by + rw [div_pow, div_add_one (by positivity), one_div_div, r1] + +lemma r1_pos : 0 < r1 z := by + dsimp only [r1] + positivity + +/-- For `c, d ∈ ℝ` with `1 ≤ d ^ 2`, we have `r1 z ≤ |c * z + d| ^ 2`. -/ +lemma r1_aux_bound (c : ℝ) {d : ℝ} (hd : 1 ≤ d ^ 2) : + r1 z ≤ (c * z.re + d) ^ 2 + (c * z.im) ^ 2 := by + have H1 : (c * z.re + d) ^ 2 + (c * z.im) ^ 2 = + c ^ 2 * (z.re ^ 2 + z.im ^ 2) + d * 2 * c * z.re + d ^ 2 := by ring + have H2 : (c ^ 2 * (z.re ^ 2 + z.im ^ 2) + d * 2 * c * z.re + d ^ 2) * (z.re ^ 2 + z.im ^ 2) + - z.im ^ 2 = (c * (z.re ^ 2 + z.im ^ 2) + d * z.re) ^ 2 + (d ^ 2 - 1) * z.im ^ 2 := by ring + rw [r1, H1, div_le_iff₀ (by positivity), ← sub_nonneg, H2] + exact add_nonneg (sq_nonneg _) (mul_nonneg (sub_nonneg.mpr hd) (sq_nonneg _)) + +/-- This function is used to give an upper bound on the summands in Eisenstein series; it is +defined by `z ↦ min z.im √(z.im ^ 2 / (z.re ^ 2 + z.im ^ 2))`. -/ +def r : ℝ := min z.im √(r1 z) + +lemma r_pos : 0 < r z := by + simp only [r, lt_min_iff, im_pos, Real.sqrt_pos, r1_pos, and_self] + +lemma r_lower_bound_on_verticalStrip {A B : ℝ} (h : 0 < B) (hz : z ∈ verticalStrip A B) : + r ⟨⟨A, B⟩, h⟩ ≤ r z := by + apply min_le_min hz.2 + rw [Real.sqrt_le_sqrt_iff (by apply (r1_pos z).le)] + simp only [r1_eq, div_pow, one_div] + rw [inv_le_inv₀ (by positivity) (by positivity), add_le_add_iff_right, ← even_two.pow_abs] + gcongr + exacts [hz.1, hz.2] + +lemma auxbound1 {c : ℝ} (d : ℝ) (hc : 1 ≤ c ^ 2) : r z ≤ ‖c * (z : ℂ) + d‖ := by + rcases z with ⟨z, hz⟩ + have H1 : z.im ≤ √((c * z.re + d) ^ 2 + (c * z).im ^ 2) := by + rw [Real.le_sqrt' hz, im_ofReal_mul, mul_pow] + exact (le_mul_of_one_le_left (sq_nonneg _) hc).trans <| le_add_of_nonneg_left (sq_nonneg _) + simpa only [r, norm_def, normSq_apply, add_re, re_ofReal_mul, coe_re, ← pow_two, add_im, mul_im, + coe_im, ofReal_im, zero_mul, add_zero, min_le_iff] using Or.inl H1 + +lemma auxbound2 (c : ℝ) {d : ℝ} (hd : 1 ≤ d ^ 2) : r z ≤ ‖c * (z : ℂ) + d‖ := by + have H1 : √(r1 z) ≤ √((c * z.re + d) ^ 2 + (c * z.im) ^ 2) := + (Real.sqrt_le_sqrt_iff (by positivity)).mpr (r1_aux_bound _ _ hd) + simpa only [r, norm_def, normSq_apply, add_re, re_ofReal_mul, coe_re, ofReal_re, ← pow_two, + add_im, im_ofReal_mul, coe_im, ofReal_im, add_zero, min_le_iff] using Or.inr H1 + +lemma div_max_sq_ge_one (x : Fin 2 → ℤ) (hx : x ≠ 0) : + 1 ≤ (x 0 / ‖x‖) ^ 2 ∨ 1 ≤ (x 1 / ‖x‖) ^ 2 := by + refine (max_choice (x 0).natAbs (x 1).natAbs).imp (fun H0 ↦ ?_) (fun H1 ↦ ?_) + · have : x 0 ≠ 0 := by + rwa [← norm_ne_zero_iff, norm_eq_max_natAbs, H0, Nat.cast_ne_zero, Int.natAbs_ne_zero] at hx + simp only [norm_eq_max_natAbs, H0, Int.cast_natAbs, Int.cast_abs, div_pow, sq_abs, ne_eq, + OfNat.ofNat_ne_zero, not_false_eq_true, pow_eq_zero_iff, Int.cast_eq_zero, this, div_self, + le_refl] + · have : x 1 ≠ 0 := by + rwa [← norm_ne_zero_iff, norm_eq_max_natAbs, H1, Nat.cast_ne_zero, Int.natAbs_ne_zero] at hx + simp only [norm_eq_max_natAbs, H1, Int.cast_natAbs, Int.cast_abs, div_pow, sq_abs, ne_eq, + OfNat.ofNat_ne_zero, not_false_eq_true, pow_eq_zero_iff, Int.cast_eq_zero, this, div_self, + le_refl] + +lemma r_mul_max_le {x : Fin 2 → ℤ} (hx : x ≠ 0) : r z * ‖x‖ ≤ ‖x 0 * (z : ℂ) + x 1‖ := by + have hn0 : ‖x‖ ≠ 0 := by rwa [norm_ne_zero_iff] + have h11 : x 0 * (z : ℂ) + x 1 = (x 0 / ‖x‖ * z + x 1 / ‖x‖) * ‖x‖ := by + rw [div_mul_eq_mul_div, ← add_div, div_mul_cancel₀ _ (mod_cast hn0)] + rw [norm_eq_max_natAbs, h11, norm_mul, norm_real, norm_norm, norm_eq_max_natAbs] + gcongr + · rcases div_max_sq_ge_one x hx with H1 | H2 + · simpa only [norm_eq_max_natAbs, ofReal_div, ofReal_intCast] using auxbound1 z (x 1 / ‖x‖) H1 + · simpa only [norm_eq_max_natAbs, ofReal_div, ofReal_intCast] using auxbound2 z (x 0 / ‖x‖) H2 + +/-- Upper bound for the summand `|c * z + d| ^ (-k)`, as a product of a function of `z` and a +function of `c, d`. -/ +lemma summand_bound {k : ℝ} (hk : 0 ≤ k) (x : Fin 2 → ℤ) : + ‖x 0 * (z : ℂ) + x 1‖ ^ (-k) ≤ (r z) ^ (-k) * ‖x‖ ^ (-k) := by + by_cases hx : x = 0 + · simp only [hx, Pi.zero_apply, Int.cast_zero, zero_mul, add_zero, norm_zero] + by_cases h : -k = 0 + · rw [h, Real.rpow_zero, Real.rpow_zero, one_mul] + · rw [Real.zero_rpow h, mul_zero] + · rw [← Real.mul_rpow (r_pos _).le (norm_nonneg _)] + exact Real.rpow_le_rpow_of_nonpos (mul_pos (r_pos _) (norm_pos_iff.mpr hx)) (r_mul_max_le z hx) + (neg_nonpos.mpr hk) + +variable {z} in +lemma summand_bound_of_mem_verticalStrip {k : ℝ} (hk : 0 ≤ k) (x : Fin 2 → ℤ) + {A B : ℝ} (hB : 0 < B) (hz : z ∈ verticalStrip A B) : + ‖x 0 * (z : ℂ) + x 1‖ ^ (-k) ≤ r ⟨⟨A, B⟩, hB⟩ ^ (-k) * ‖x‖ ^ (-k) := by + refine (summand_bound z hk x).trans (mul_le_mul_of_nonneg_right ?_ (by positivity)) + exact Real.rpow_le_rpow_of_nonpos (r_pos _) (r_lower_bound_on_verticalStrip z hB hz) + (neg_nonpos.mpr hk) + +end bounding_functions + +/-- The function `ℤ ^ 2 → ℝ` given by `x ↦ ‖x‖ ^ (-k)` is summable if `2 < k`. We prove this by +splitting into boxes using `Finset.box`. -/ +lemma summable_one_div_norm_rpow {k : ℝ} (hk : 2 < k) : + Summable fun (x : Fin 2 → ℤ) ↦ ‖x‖ ^ (-k) := by + rw [← (finTwoArrowEquiv _).symm.summable_iff, summable_partition _ Int.existsUnique_mem_box] + · simp only [finTwoArrowEquiv_symm_apply, Function.comp_def] + refine ⟨fun n ↦ (hasSum_fintype (β := box (α := ℤ × ℤ) n) _).summable, ?_⟩ + suffices Summable fun n : ℕ ↦ ∑' (_ : box (α := ℤ × ℤ) n), (n : ℝ) ^ (-k) by + refine this.congr fun n ↦ tsum_congr fun p ↦ ?_ + simp only [← Int.mem_box.mp p.2, Nat.cast_max, norm_eq_max_natAbs, Matrix.cons_val_zero, + Matrix.cons_val_one, Matrix.head_cons] + simp only [tsum_fintype, univ_eq_attach, sum_const, card_attach, nsmul_eq_mul] + apply ((Real.summable_nat_rpow.mpr (by linarith : 1 - k < -1)).mul_left + 8).of_norm_bounded_eventually_nat + filter_upwards [Filter.eventually_gt_atTop 0] with n hn + rw [Int.card_box hn.ne', Real.norm_of_nonneg (by positivity), sub_eq_add_neg, + Real.rpow_add (Nat.cast_pos.mpr hn), Real.rpow_one, Nat.cast_mul, Nat.cast_ofNat, mul_assoc] + · exact fun n ↦ Real.rpow_nonneg (norm_nonneg _) _ + +end EisensteinSeries diff --git a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/UniformConvergence.lean b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/UniformConvergence.lean index a0c229e96e5f37..4822ff34460528 100644 --- a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/UniformConvergence.lean +++ b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/UniformConvergence.lean @@ -4,11 +4,9 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Chris Birkbeck, David Loeffler -/ -import Mathlib.Analysis.Complex.UpperHalfPlane.Topology import Mathlib.Analysis.NormedSpace.FunctionSeries -import Mathlib.Analysis.PSeries -import Mathlib.Order.Interval.Finset.Box import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Defs +import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Summable /-! # Uniform convergence of Eisenstein series @@ -33,137 +31,10 @@ open Complex UpperHalfPlane Set Finset CongruenceSubgroup Topology open scoped UpperHalfPlane - variable (z : ℍ) namespace EisensteinSeries -lemma norm_eq_max_natAbs (x : Fin 2 → ℤ) : ‖x‖ = max (x 0).natAbs (x 1).natAbs := by - rw [← coe_nnnorm, ← NNReal.coe_natCast, NNReal.coe_inj, Nat.cast_max] - refine eq_of_forall_ge_iff fun c ↦ ?_ - simp only [pi_nnnorm_le_iff, Fin.forall_fin_two, max_le_iff, NNReal.natCast_natAbs] - -section bounding_functions - -/-- Auxiliary function used for bounding Eisenstein series, defined as - `z.im ^ 2 / (z.re ^ 2 + z.im ^ 2)`. -/ -def r1 : ℝ := z.im ^ 2 / (z.re ^ 2 + z.im ^ 2) - -lemma r1_eq : r1 z = 1 / ((z.re / z.im) ^ 2 + 1) := by - rw [div_pow, div_add_one (by positivity), one_div_div, r1] - -lemma r1_pos : 0 < r1 z := by - dsimp only [r1] - positivity - -/-- For `c, d ∈ ℝ` with `1 ≤ d ^ 2`, we have `r1 z ≤ |c * z + d| ^ 2`. -/ -lemma r1_aux_bound (c : ℝ) {d : ℝ} (hd : 1 ≤ d ^ 2) : - r1 z ≤ (c * z.re + d) ^ 2 + (c * z.im) ^ 2 := by - have H1 : (c * z.re + d) ^ 2 + (c * z.im) ^ 2 = - c ^ 2 * (z.re ^ 2 + z.im ^ 2) + d * 2 * c * z.re + d ^ 2 := by ring - have H2 : (c ^ 2 * (z.re ^ 2 + z.im ^ 2) + d * 2 * c * z.re + d ^ 2) * (z.re ^ 2 + z.im ^ 2) - - z.im ^ 2 = (c * (z.re ^ 2 + z.im ^ 2) + d * z.re) ^ 2 + (d ^ 2 - 1) * z.im ^ 2 := by ring - rw [r1, H1, div_le_iff₀ (by positivity), ← sub_nonneg, H2] - exact add_nonneg (sq_nonneg _) (mul_nonneg (sub_nonneg.mpr hd) (sq_nonneg _)) - -/-- This function is used to give an upper bound on the summands in Eisenstein series; it is -defined by `z ↦ min z.im √(z.im ^ 2 / (z.re ^ 2 + z.im ^ 2))`. -/ -def r : ℝ := min z.im √(r1 z) - -lemma r_pos : 0 < r z := by - simp only [r, lt_min_iff, im_pos, Real.sqrt_pos, r1_pos, and_self] - -lemma r_lower_bound_on_verticalStrip {A B : ℝ} (h : 0 < B) (hz : z ∈ verticalStrip A B) : - r ⟨⟨A, B⟩, h⟩ ≤ r z := by - apply min_le_min hz.2 - rw [Real.sqrt_le_sqrt_iff (by apply (r1_pos z).le)] - simp only [r1_eq, div_pow, one_div] - rw [inv_le_inv₀ (by positivity) (by positivity), add_le_add_iff_right, ← even_two.pow_abs] - gcongr - exacts [hz.1, hz.2] - -lemma auxbound1 {c : ℝ} (d : ℝ) (hc : 1 ≤ c ^ 2) : r z ≤ ‖c * (z : ℂ) + d‖ := by - rcases z with ⟨z, hz⟩ - have H1 : z.im ≤ √((c * z.re + d) ^ 2 + (c * z).im ^ 2) := by - rw [Real.le_sqrt' hz, im_ofReal_mul, mul_pow] - exact (le_mul_of_one_le_left (sq_nonneg _) hc).trans <| le_add_of_nonneg_left (sq_nonneg _) - simpa only [r, norm_def, normSq_apply, add_re, re_ofReal_mul, coe_re, ← pow_two, add_im, mul_im, - coe_im, ofReal_im, zero_mul, add_zero, min_le_iff] using Or.inl H1 - -lemma auxbound2 (c : ℝ) {d : ℝ} (hd : 1 ≤ d ^ 2) : r z ≤ ‖c * (z : ℂ) + d‖ := by - have H1 : √(r1 z) ≤ √((c * z.re + d) ^ 2 + (c * z.im) ^ 2) := - (Real.sqrt_le_sqrt_iff (by positivity)).mpr (r1_aux_bound _ _ hd) - simpa only [r, norm_def, normSq_apply, add_re, re_ofReal_mul, coe_re, ofReal_re, ← pow_two, - add_im, im_ofReal_mul, coe_im, ofReal_im, add_zero, min_le_iff] using Or.inr H1 - -lemma div_max_sq_ge_one (x : Fin 2 → ℤ) (hx : x ≠ 0) : - 1 ≤ (x 0 / ‖x‖) ^ 2 ∨ 1 ≤ (x 1 / ‖x‖) ^ 2 := by - refine (max_choice (x 0).natAbs (x 1).natAbs).imp (fun H0 ↦ ?_) (fun H1 ↦ ?_) - · have : x 0 ≠ 0 := by - rwa [← norm_ne_zero_iff, norm_eq_max_natAbs, H0, Nat.cast_ne_zero, Int.natAbs_ne_zero] at hx - simp only [norm_eq_max_natAbs, H0, Int.cast_natAbs, Int.cast_abs, div_pow, sq_abs, ne_eq, - OfNat.ofNat_ne_zero, not_false_eq_true, pow_eq_zero_iff, Int.cast_eq_zero, this, div_self, - le_refl] - · have : x 1 ≠ 0 := by - rwa [← norm_ne_zero_iff, norm_eq_max_natAbs, H1, Nat.cast_ne_zero, Int.natAbs_ne_zero] at hx - simp only [norm_eq_max_natAbs, H1, Int.cast_natAbs, Int.cast_abs, div_pow, sq_abs, ne_eq, - OfNat.ofNat_ne_zero, not_false_eq_true, pow_eq_zero_iff, Int.cast_eq_zero, this, div_self, - le_refl] - -lemma r_mul_max_le {x : Fin 2 → ℤ} (hx : x ≠ 0) : r z * ‖x‖ ≤ ‖x 0 * (z : ℂ) + x 1‖ := by - have hn0 : ‖x‖ ≠ 0 := by rwa [norm_ne_zero_iff] - have h11 : x 0 * (z : ℂ) + x 1 = (x 0 / ‖x‖ * z + x 1 / ‖x‖) * ‖x‖ := by - rw [div_mul_eq_mul_div, ← add_div, div_mul_cancel₀ _ (mod_cast hn0)] - rw [norm_eq_max_natAbs, h11, norm_mul, norm_real, norm_norm, norm_eq_max_natAbs] - gcongr - · rcases div_max_sq_ge_one x hx with H1 | H2 - · simpa only [norm_eq_max_natAbs, ofReal_div, ofReal_intCast] using auxbound1 z (x 1 / ‖x‖) H1 - · simpa only [norm_eq_max_natAbs, ofReal_div, ofReal_intCast] using auxbound2 z (x 0 / ‖x‖) H2 - -/-- Upper bound for the summand `|c * z + d| ^ (-k)`, as a product of a function of `z` and a -function of `c, d`. -/ -lemma summand_bound {k : ℝ} (hk : 0 ≤ k) (x : Fin 2 → ℤ) : - ‖x 0 * (z : ℂ) + x 1‖ ^ (-k) ≤ (r z) ^ (-k) * ‖x‖ ^ (-k) := by - by_cases hx : x = 0 - · simp only [hx, Pi.zero_apply, Int.cast_zero, zero_mul, add_zero, norm_zero] - by_cases h : -k = 0 - · rw [h, Real.rpow_zero, Real.rpow_zero, one_mul] - · rw [Real.zero_rpow h, mul_zero] - · rw [← Real.mul_rpow (r_pos _).le (norm_nonneg _)] - exact Real.rpow_le_rpow_of_nonpos (mul_pos (r_pos _) (norm_pos_iff.mpr hx)) (r_mul_max_le z hx) - (neg_nonpos.mpr hk) - -variable {z} in -lemma summand_bound_of_mem_verticalStrip {k : ℝ} (hk : 0 ≤ k) (x : Fin 2 → ℤ) - {A B : ℝ} (hB : 0 < B) (hz : z ∈ verticalStrip A B) : - ‖x 0 * (z : ℂ) + x 1‖ ^ (-k) ≤ r ⟨⟨A, B⟩, hB⟩ ^ (-k) * ‖x‖ ^ (-k) := by - refine (summand_bound z hk x).trans (mul_le_mul_of_nonneg_right ?_ (by positivity)) - exact Real.rpow_le_rpow_of_nonpos (r_pos _) (r_lower_bound_on_verticalStrip z hB hz) - (neg_nonpos.mpr hk) - -end bounding_functions - -section summability - -/-- The function `ℤ ^ 2 → ℝ` given by `x ↦ ‖x‖ ^ (-k)` is summable if `2 < k`. We prove this by -splitting into boxes using `Finset.box`. -/ -lemma summable_one_div_norm_rpow {k : ℝ} (hk : 2 < k) : - Summable fun (x : Fin 2 → ℤ) ↦ ‖x‖ ^ (-k) := by - rw [← (finTwoArrowEquiv _).symm.summable_iff, summable_partition _ Int.existsUnique_mem_box] - · simp only [finTwoArrowEquiv_symm_apply, Function.comp_def] - refine ⟨fun n ↦ (hasSum_fintype (β := box (α := ℤ × ℤ) n) _).summable, ?_⟩ - suffices Summable fun n : ℕ ↦ ∑' (_ : box (α := ℤ × ℤ) n), (n : ℝ) ^ (-k) by - refine this.congr fun n ↦ tsum_congr fun p ↦ ?_ - simp only [← Int.mem_box.mp p.2, Nat.cast_max, norm_eq_max_natAbs, Matrix.cons_val_zero, - Matrix.cons_val_one, Matrix.head_cons] - simp only [tsum_fintype, univ_eq_attach, sum_const, card_attach, nsmul_eq_mul] - apply ((Real.summable_nat_rpow.mpr (by linarith : 1 - k < -1)).mul_left - 8).of_norm_bounded_eventually_nat - filter_upwards [Filter.eventually_gt_atTop 0] with n hn - rw [Int.card_box hn.ne', Real.norm_of_nonneg (by positivity), sub_eq_add_neg, - Real.rpow_add (Nat.cast_pos.mpr hn), Real.rpow_one, Nat.cast_mul, Nat.cast_ofNat, mul_assoc] - · exact fun n ↦ Real.rpow_nonneg (norm_nonneg _) _ - /-- The sum defining the Eisenstein series (of weight `k` and level `Γ(N)` with congruence condition `a : Fin 2 → ZMod N`) converges locally uniformly on `ℍ`. -/ theorem eisensteinSeries_tendstoLocallyUniformly {k : ℤ} (hk : 3 ≤ k) {N : ℕ} (a : Fin 2 → ZMod N) : @@ -193,6 +64,4 @@ lemma eisensteinSeries_tendstoLocallyUniformlyOn {k : ℤ} {N : ℕ} (hk : 3 ≤ · simp only [IsOpenEmbedding.toPartialHomeomorph_target, Set.top_eq_univ, mapsTo_range_iff, Set.mem_univ, forall_const] -end summability - end EisensteinSeries diff --git a/Mathlib/NumberTheory/NumberField/ClassNumber.lean b/Mathlib/NumberTheory/NumberField/ClassNumber.lean index bdd843116f899d..d796ef7054799e 100644 --- a/Mathlib/NumberTheory/NumberField/ClassNumber.lean +++ b/Mathlib/NumberTheory/NumberField/ClassNumber.lean @@ -18,14 +18,14 @@ on the class number. ## Main definitions - `NumberField.classNumber`: the class number of a number field is the (finite) -cardinality of the class group of its ring of integers + cardinality of the class group of its ring of integers - `isPrincipalIdealRing_of_isPrincipal_of_pow_inertiaDeg_le_of_mem_primesOver_of_mem_Icc`: let `K` -be a number field and let `M K` be the Minkowski bound of `K` (by definition it is -`(4 / π) ^ nrComplexPlaces K * ((finrank ℚ K)! / (finrank ℚ K) ^ (finrank ℚ K) * √|discr K|)`). -To show that `𝓞 K` is a PID it is enough to show that, for all (natural) primes -`p ∈ Finset.Icc 1 ⌊(M K)⌋₊`, all ideals `P` above `p` such that -`p ^ (span ({p}).inertiaDeg P) ≤ ⌊(M K)⌋₊` are principal. This is the standard technique to prove -that `𝓞 K` is principal, see [marcus1977number], discussion after Theorem 37. + be a number field and let `M K` be the Minkowski bound of `K` (by definition it is + `(4 / π) ^ nrComplexPlaces K * ((finrank ℚ K)! / (finrank ℚ K) ^ (finrank ℚ K) * √|discr K|)`). + To show that `𝓞 K` is a PID it is enough to show that, for all (natural) primes + `p ∈ Finset.Icc 1 ⌊(M K)⌋₊`, all ideals `P` above `p` such that + `p ^ (span ({p}).inertiaDeg P) ≤ ⌊(M K)⌋₊` are principal. This is the standard technique to prove + that `𝓞 K` is principal, see [marcus1977number], discussion after Theorem 37. The way this theorem should be used is to first compute `⌊(M K)⌋₊` and then to use `fin_cases` to deal with the finite number of primes `p` in the interval. diff --git a/Mathlib/NumberTheory/NumberField/Discriminant/Defs.lean b/Mathlib/NumberTheory/NumberField/Discriminant/Defs.lean index 05d05718b66165..b0ad3012e7db7a 100644 --- a/Mathlib/NumberTheory/NumberField/Discriminant/Defs.lean +++ b/Mathlib/NumberTheory/NumberField/Discriminant/Defs.lean @@ -74,7 +74,7 @@ theorem numberField_discr : discr ℚ = 1 := by _ = Algebra.trace ℤ (𝓞 ℚ) (b default * b default) := by rw [Algebra.discr_def, Matrix.det_unique, Algebra.traceMatrix_apply, Algebra.traceForm_apply] _ = Algebra.trace ℤ (𝓞 ℚ) 1 := by - rw [Basis.map_apply, RingEquiv.toAddEquiv_eq_coe, AddEquiv.toIntLinearEquiv_symm, + rw [Basis.map_apply, RingEquiv.toAddEquiv_eq_coe, ← AddEquiv.toIntLinearEquiv_symm, AddEquiv.coe_toIntLinearEquiv, Basis.singleton_apply, show (AddEquiv.symm ↑ringOfIntegersEquiv) (1 : ℤ) = ringOfIntegersEquiv.symm 1 by rfl, map_one, mul_one] diff --git a/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean b/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean new file mode 100644 index 00000000000000..29d29e63e998d0 --- /dev/null +++ b/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean @@ -0,0 +1,71 @@ +/- +Copyright (c) 2025 Andrew Yang. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Andrew Yang +-/ +import Mathlib.NumberTheory.NumberField.Discriminant.Defs +import Mathlib.RingTheory.DedekindDomain.Factorization +import Mathlib.RingTheory.DedekindDomain.Different +import Mathlib.RingTheory.Ideal.Norm.AbsNorm +import Mathlib.Tactic.Qify + +/-! + +# (Absolute) Discriminant and Different Ideal + +## Main results +- `NumberField.absNorm_differentIdeal`: + The norm of `differentIdeal ℤ 𝒪` is the absolute discriminant. + +-/ + +variable {K 𝒪 : Type*} [Field K] [NumberField K] [CommRing 𝒪] [Algebra 𝒪 K] +variable [IsFractionRing 𝒪 K] [IsIntegralClosure 𝒪 ℤ K] [IsDedekindDomain 𝒪] [CharZero 𝒪] +variable [Module.Finite ℤ 𝒪] + +open nonZeroDivisors + +lemma NumberField.absNorm_differentIdeal : (differentIdeal ℤ 𝒪).absNorm = (discr K).natAbs := by + refine (differentIdeal ℤ 𝒪).toAddSubgroup.relindex_top_right.symm.trans ?_ + rw [← Submodule.comap_map_eq_of_injective (f := Algebra.linearMap 𝒪 K) + (FaithfulSMul.algebraMap_injective 𝒪 K) (differentIdeal ℤ 𝒪)] + refine (AddSubgroup.relindex_comap (IsLocalization.coeSubmodule K + (differentIdeal ℤ 𝒪)).toAddSubgroup (algebraMap 𝒪 K).toAddMonoidHom ⊤).trans ?_ + have := FractionalIdeal.quotientEquiv (R := 𝒪) (K := K) 1 (differentIdeal ℤ 𝒪) + (differentIdeal ℤ 𝒪)⁻¹ 1 (by simp [differentIdeal_ne_bot]) FractionalIdeal.coeIdeal_le_one + (le_inv_of_le_inv₀ (by simp [pos_iff_ne_zero, differentIdeal_ne_bot]) + (by simpa using FractionalIdeal.coeIdeal_le_one)) one_ne_zero one_ne_zero + have := Nat.card_congr this.toEquiv + refine this.trans ?_ + rw [FractionalIdeal.coe_one, coeIdeal_differentIdeal (K := ℚ), inv_inv] + let b := integralBasis K + let b' := (Algebra.traceForm ℚ K).dualBasis (traceForm_nondegenerate ℚ K) b + have hb : Submodule.span ℤ (Set.range b) = (1 : Submodule 𝒪 K).restrictScalars ℤ := by + ext + let e := IsIntegralClosure.equiv ℤ (RingOfIntegers K) K 𝒪 + simpa [e.symm.exists_congr_left, e] using mem_span_integralBasis K + qify + refine (AddSubgroup.relindex_eq_abs_det (1 : Submodule 𝒪 K).toAddSubgroup (FractionalIdeal.dual + ℤ ℚ 1 : FractionalIdeal 𝒪⁰ K).coeToSubmodule.toAddSubgroup ?_ b b' ?_ ?_).trans ?_ + · rw [Submodule.toAddSubgroup_le, ← FractionalIdeal.coe_one] + exact FractionalIdeal.one_le_dual_one ℤ ℚ (L := K) (B := 𝒪) + · apply AddSubgroup.toIntSubmodule.injective + rw [AddSubgroup.toIntSubmodule_closure, hb, Submodule.toIntSubmodule_toAddSubgroup] + · apply AddSubgroup.toIntSubmodule.injective + rw [AddSubgroup.toIntSubmodule_closure, ← LinearMap.BilinForm.dualSubmodule_span_of_basis, hb] + simp + · simp only [Basis.det_apply, discr, Algebra.discr] + rw [← eq_intCast (algebraMap ℤ ℚ), RingHom.map_det] + congr! 2 + ext i j + simp [b', Basis.toMatrix_apply, mul_comm (RingOfIntegers.basis K i), + b, integralBasis_apply, ← map_mul, Algebra.trace_localization ℤ ℤ⁰] + +lemma NumberField.discr_mem_differentIdeal : ↑(discr K) ∈ differentIdeal ℤ 𝒪 := by + have := (differentIdeal ℤ 𝒪).absNorm_mem + cases (discr K).natAbs_eq with + | inl h => + rwa [absNorm_differentIdeal (K := K), ← Int.cast_natCast, ← h] at this + | inr h => + rwa [absNorm_differentIdeal (K := K), ← Int.cast_natCast, Int.eq_neg_comm.mp h, + Int.cast_neg, neg_mem_iff] at this diff --git a/Mathlib/NumberTheory/NumberField/Units/Regulator.lean b/Mathlib/NumberTheory/NumberField/Units/Regulator.lean index 70c6391ac0da8b..b4edf1323df8a4 100644 --- a/Mathlib/NumberTheory/NumberField/Units/Regulator.lean +++ b/Mathlib/NumberTheory/NumberField/Units/Regulator.lean @@ -18,9 +18,10 @@ We define and prove basic results about the regulator of a number field `K`. * `NumberField.Units.regulator`: the regulator of the number field `K`. -* `Number.Field.Units.regulator_eq_det`: For any infinite place `w'`, the regulator is equal to - the absolute value of the determinant of the matrix `(mult w * log w (fundSystem K i)))_i, w` - where `w` runs through the infinite places distinct from `w'`. +* `Number.Field.Units.regOfFamily_eq_det`: For any infinite place `w'`, the regulator of the +family `u` is equal to the absolute value of the determinant of the matrix +`(mult w * log w (u i)))_i, w` where `w` runs through the infinite places distinct from `w'`. + ## Tags number field, units, regulator @@ -75,6 +76,46 @@ theorem basisOfIsMaxRank_apply {u : Fin (rank K) → (𝓞 K)ˣ} (hu : IsMaxRank simp [basisOfIsMaxRank, Basis.coe_reindex, Equiv.symm_symm, Function.comp_apply, coe_basisOfPiSpaceOfLinearIndependent] +theorem finiteIndex_iff_sup_torsion_finiteIndex (s : Subgroup (𝓞 K)ˣ) : + s.FiniteIndex ↔ (s ⊔ torsion K).FiniteIndex := by + refine ⟨fun h ↦ Subgroup.finiteIndex_of_le le_sup_left, fun h ↦ ?_⟩ + rw [Subgroup.finiteIndex_iff, ← Subgroup.relindex_mul_index (le_sup_left : s ≤ s ⊔ torsion K)] + refine Nat.mul_ne_zero ?_ (Subgroup.finiteIndex_iff.mp h) + rw [Subgroup.relindex_sup_left] + exact Subgroup.FiniteIndex.index_ne_zero + +open Subgroup in +/-- +A family of units is of maximal rank iff the index of the subgroup it generates has finite index. +-/ +theorem isMaxRank_iff_closure_finiteIndex {u : Fin (rank K) → (𝓞 K)ˣ} : + IsMaxRank u ↔ (closure (Set.range u)).FiniteIndex := by + classical + have h₁ : (closure (Set.range u) ⊔ torsion K).index ≠ 0 ↔ + Finite (unitLattice K ⧸ span ℤ (Set.range ((logEmbeddingEquiv K) ∘ Additive.toMul.symm ∘ + QuotientGroup.mk ∘ u))) := by + change _ ↔ Finite ((unitLattice K).toAddSubgroup ⧸ (span ℤ (Set.range _)).toAddSubgroup) + rw [← AddSubgroup.index_ne_zero_iff_finite] + have := index_map (closure (Set.range u)) (QuotientGroup.mk' (torsion K)) + rw [QuotientGroup.ker_mk', QuotientGroup.range_mk', index_top, mul_one] at this + rw [← this, ← index_toAddSubgroup, ← AddSubgroup.index_map_equiv + _ (logEmbeddingEquiv K).toAddEquiv, Set.range_comp, ← map_span (logEmbeddingEquiv K), + ← map_coe_toLinearMap, map_toAddSubgroup, span_int_eq_addSubgroup_closure, + MonoidHom.map_closure, toAddSubgroup_closure, Set.range_comp, Set.range_comp, + QuotientGroup.coe_mk', Set.preimage_equiv_eq_image_symm] + exact Iff.rfl + have h₂ : DiscreteTopology + (span ℤ (Set.range fun i ↦ (logEmbedding K) (Additive.ofMul (u i)))) := by + refine DiscreteTopology.of_subset (inferInstance : DiscreteTopology (unitLattice K)) ?_ + rw [SetLike.coe_subset_coe, Submodule.span_le] + rintro _ ⟨i, rfl⟩ + exact ⟨Additive.ofMul (u i), mem_top, rfl⟩ + rw [finiteIndex_iff_sup_torsion_finiteIndex, finiteIndex_iff, h₁, finiteQuotient_iff, + unitLattice_rank, ← Set.finrank, IsMaxRank, linearIndependent_iff_card_eq_finrank_span, + Real.finrank_eq_int_finrank_of_discrete h₂, Set.finrank, Set.finrank, ← finrank_map_subtype_eq, + map_span, ← Set.range_comp', eq_comm] + simp + open scoped Classical in /-- The regulator of a family of units of `K`. diff --git a/Mathlib/NumberTheory/Padics/RingHoms.lean b/Mathlib/NumberTheory/Padics/RingHoms.lean index 897e7c0aeae145..af5b69147984fa 100644 --- a/Mathlib/NumberTheory/Padics/RingHoms.lean +++ b/Mathlib/NumberTheory/Padics/RingHoms.lean @@ -119,7 +119,7 @@ theorem norm_sub_modPart (h : ‖(r : ℚ_[p])‖ ≤ 1) : ‖(⟨r, h⟩ - modP let n := modPart p r rw [norm_lt_one_iff_dvd, ← (isUnit_den r h).dvd_mul_right] suffices ↑p ∣ r.num - n * r.den by - convert (Int.castRingHom ℤ_[p]).map_dvd this + convert (map_dvd (Int.castRingHom ℤ_[p])) this simp only [n, sub_mul, Int.cast_natCast, eq_intCast, Int.cast_mul, sub_left_inj, Int.cast_sub] apply Subtype.coe_injective @@ -604,7 +604,7 @@ theorem lift_sub_val_mem_span (r : R) (n : ℕ) : apply Ideal.add_mem _ _ this rw [Ideal.mem_span_singleton] convert - (Int.castRingHom ℤ_[p]).map_dvd (pow_dvd_nthHom_sub f_compat r n (max n k) (le_max_left _ _)) + map_dvd (Int.castRingHom ℤ_[p]) (pow_dvd_nthHom_sub f_compat r n (max n k) (le_max_left _ _)) · simp · simp [nthHom] diff --git a/Mathlib/NumberTheory/RamificationInertia/Unramified.lean b/Mathlib/NumberTheory/RamificationInertia/Unramified.lean index 0d9c4e0c8a889b..0cf5b79f6ace64 100644 --- a/Mathlib/NumberTheory/RamificationInertia/Unramified.lean +++ b/Mathlib/NumberTheory/RamificationInertia/Unramified.lean @@ -69,7 +69,7 @@ lemma IsUnramifiedAt.of_liesOver_of_ne_bot variable (R) in /-- -Up to techinical conditions, If `T/S/R` is a tower of algebras, `P` is a prime of `T` unramified +Up to technical conditions, If `T/S/R` is a tower of algebras, `P` is a prime of `T` unramified in `R`, then `P ∩ S` (as a prime of `S`) is also unramified in `R`. -/ lemma Algebra.IsUnramifiedAt.of_liesOver diff --git a/Mathlib/NumberTheory/SumPrimeReciprocals.lean b/Mathlib/NumberTheory/SumPrimeReciprocals.lean index 9772c89445487f..9df907e65a89a9 100644 --- a/Mathlib/NumberTheory/SumPrimeReciprocals.lean +++ b/Mathlib/NumberTheory/SumPrimeReciprocals.lean @@ -76,8 +76,8 @@ theorem not_summable_one_div_on_primes : (fun n _ ↦ indicator_nonneg (fun p _ ↦ by positivity) _) h' using 2 with p hp obtain ⟨hp₁, hp₂⟩ := mem_setOf_eq ▸ Finset.mem_sdiff.mp hp have hpp := prime_of_mem_primesBelow hp₁ - refine (indicator_of_mem (mem_def.mpr ⟨hpp, ?_⟩) fun n : ℕ ↦ (1 / n : ℝ)).symm - exact not_lt.mp <| (not_and_or.mp <| (not_congr mem_primesBelow).mp hp₂).neg_resolve_right hpp + refine (indicator_of_mem ?_ fun n : ℕ ↦ (1 / n : ℝ)).symm + exact ⟨hpp, by simpa [primesBelow, hpp] using hp₂⟩ /-- The sum over the reciprocals of the primes diverges. -/ theorem Nat.Primes.not_summable_one_div : ¬ Summable (fun p : Nat.Primes ↦ (1 / p : ℝ)) := by diff --git a/Mathlib/Order/Basic.lean b/Mathlib/Order/Basic.lean index 1bcf321c6dfb7c..5c28e93a9e7dc5 100644 --- a/Mathlib/Order/Basic.lean +++ b/Mathlib/Order/Basic.lean @@ -187,6 +187,32 @@ theorem forall_le_iff_ge : (∀ ⦃c⦄, a ≤ c → b ≤ c) ↔ b ≤ a := theorem le_implies_le_of_le_of_le (hca : c ≤ a) (hbd : b ≤ d) : a ≤ b → c ≤ d := fun hab ↦ (hca.trans hab).trans hbd +namespace GCongr + +-- The `≤`-transitivity lemmas aren't strictly needed +-- but it is a very common case, so we tag them anyways +@[gcongr] theorem le_imp_le (h₁ : c ≤ a) (h₂ : b ≤ d) : a ≤ b → c ≤ d := + fun h => le_trans (le_trans h₁ h) h₂ + +attribute [gcongr] le_trans ge_trans + +@[gcongr] theorem lt_imp_lt (h₁ : c ≤ a) (h₂ : b ≤ d) : a < b → c < d := + fun h => lt_of_lt_of_le (lt_of_le_of_lt h₁ h) h₂ + +attribute [gcongr] lt_of_le_of_lt lt_of_le_of_lt' + +@[gcongr] theorem gt_imp_gt (h₁ : a ≤ c) (h₂ : d ≤ b) : a > b → c > d := lt_imp_lt h₂ h₁ + +@[gcongr] theorem gt_imp_gt_left (h : a ≤ b) : c > b → c > a := lt_of_le_of_lt h + +@[gcongr] theorem gt_imp_gt_right (h : b ≤ a) : b > c → a > c := lt_of_le_of_lt' h + +/-- See if the term is `a < b` and the goal is `a ≤ b`. -/ +@[gcongr_forward] def exactLeOfLt : Mathlib.Tactic.GCongr.ForwardExt where + eval h goal := do goal.assignIfDefEq (← Lean.Meta.mkAppM ``le_of_lt #[h]) + +end GCongr + end Preorder /-! ### Partial order -/ @@ -1058,11 +1084,15 @@ theorem mk_le_mk [LE α] {p : α → Prop} {x y : α} {hx : p x} {hy : p y} : (⟨x, hx⟩ : Subtype p) ≤ ⟨y, hy⟩ ↔ x ≤ y := Iff.rfl +@[gcongr] alias ⟨_, GCongr.mk_le_mk⟩ := mk_le_mk + @[simp] theorem mk_lt_mk [LT α] {p : α → Prop} {x y : α} {hx : p x} {hy : p y} : (⟨x, hx⟩ : Subtype p) < ⟨y, hy⟩ ↔ x < y := Iff.rfl +@[gcongr] alias ⟨_, GCongr.mk_lt_mk⟩ := mk_lt_mk + @[simp, norm_cast] theorem coe_le_coe [LE α] {p : α → Prop} {x y : Subtype p} : (x : α) ≤ y ↔ x ≤ y := Iff.rfl diff --git a/Mathlib/Order/Bounds/Basic.lean b/Mathlib/Order/Bounds/Basic.lean index 1b67b24a752a82..8cc3310594aa60 100644 --- a/Mathlib/Order/Bounds/Basic.lean +++ b/Mathlib/Order/Bounds/Basic.lean @@ -198,11 +198,11 @@ theorem lowerBounds_mono ⦃s t : Set α⦄ (hst : s ⊆ t) ⦃a b⦄ (hab : a lowerBounds_mono_set hst <| lowerBounds_mono_mem hab hb /-- If `s ⊆ t` and `t` is bounded above, then so is `s`. -/ -theorem BddAbove.mono ⦃s t : Set α⦄ (h : s ⊆ t) : BddAbove t → BddAbove s := +@[gcongr] theorem BddAbove.mono ⦃s t : Set α⦄ (h : s ⊆ t) : BddAbove t → BddAbove s := Nonempty.mono <| upperBounds_mono_set h /-- If `s ⊆ t` and `t` is bounded below, then so is `s`. -/ -theorem BddBelow.mono ⦃s t : Set α⦄ (h : s ⊆ t) : BddBelow t → BddBelow s := +@[gcongr] theorem BddBelow.mono ⦃s t : Set α⦄ (h : s ⊆ t) : BddBelow t → BddBelow s := Nonempty.mono <| lowerBounds_mono_set h /-- If `a` is a least upper bound for sets `s` and `p`, then it is a least upper bound for any diff --git a/Mathlib/Order/Category/BddLat.lean b/Mathlib/Order/Category/BddLat.lean index cd87670def7f65..77cd9f1ec78dd1 100644 --- a/Mathlib/Order/Category/BddLat.lean +++ b/Mathlib/Order/Category/BddLat.lean @@ -215,8 +215,7 @@ def latToBddLatForgetAdjunction : latToBddLat.{u} ⊣ forget₂ BddLat Lat := match a with | none => f.hom.map_top'.symm | some none => f.hom.map_bot'.symm - | some (some _) => rfl - right_inv := fun _ => Lat.ext fun _ => rfl } + | some (some _) => rfl } homEquiv_naturality_left_symm := fun _ _ => BddLat.ext fun a => match a with diff --git a/Mathlib/Order/Category/PartOrd.lean b/Mathlib/Order/Category/PartOrd.lean index 096e678f85f07d..545372a30ba02d 100644 --- a/Mathlib/Order/Category/PartOrd.lean +++ b/Mathlib/Order/Category/PartOrd.lean @@ -187,8 +187,7 @@ def preordToPartOrdForgetAdjunction : invFun f := PartOrd.ofHom ⟨fun a => Quotient.liftOn' a f (fun _ _ h => (AntisymmRel.image h f.hom.mono).eq), fun a b => Quotient.inductionOn₂' a b fun _ _ h => f.hom.mono h⟩ - left_inv _ := PartOrd.ext fun x => Quotient.inductionOn' x fun _ => rfl - right_inv _ := Preord.ext fun _ => rfl } + left_inv _ := PartOrd.ext fun x => Quotient.inductionOn' x fun _ => rfl } homEquiv_naturality_left_symm _ _ := PartOrd.ext fun x => Quotient.inductionOn' x fun _ => rfl } diff --git a/Mathlib/Order/Circular.lean b/Mathlib/Order/Circular.lean index 0b5858a1329d12..13df9c95404e12 100644 --- a/Mathlib/Order/Circular.lean +++ b/Mathlib/Order/Circular.lean @@ -3,7 +3,7 @@ Copyright (c) 2021 Yaël Dillies. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Yaël Dillies -/ -import Mathlib.Data.Set.Basic +import Mathlib.Order.Lattice import Mathlib.Tactic.Order /-! diff --git a/Mathlib/Order/CompleteLattice/Defs.lean b/Mathlib/Order/CompleteLattice/Defs.lean index 5c5def2798baf5..3be2da3e85b4ec 100644 --- a/Mathlib/Order/CompleteLattice/Defs.lean +++ b/Mathlib/Order/CompleteLattice/Defs.lean @@ -370,6 +370,12 @@ theorem lt_iSup_iff {f : ι → α} : a < iSup f ↔ ∃ i, a < f i := theorem iInf_lt_iff {f : ι → α} : iInf f < a ↔ ∃ i, f i < a := sInf_lt_iff.trans exists_range_iff +theorem lt_biSup_iff {s : Set β} {f : β → α} : a < ⨆ i ∈ s, f i ↔ ∃ i ∈ s, a < f i := by + simp [lt_iSup_iff] + +theorem biInf_lt_iff {s : Set β} {f : β → α} : ⨅ i ∈ s, f i < a ↔ ∃ i ∈ s, f i < a := by + simp [iInf_lt_iff] + end CompleteLinearOrder end diff --git a/Mathlib/Order/CompleteLattice/Group.lean b/Mathlib/Order/CompleteLattice/Group.lean new file mode 100644 index 00000000000000..f9e2f03f2cc182 --- /dev/null +++ b/Mathlib/Order/CompleteLattice/Group.lean @@ -0,0 +1,35 @@ +/- +Copyright (c) 2025 Jireh Loreaux. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jireh Loreaux +-/ +import Mathlib.Algebra.Order.Monoid.Unbundled.Basic +import Mathlib.Algebra.Order.Monoid.Unbundled.OrderDual +import Mathlib.Order.CompleteLattice.Basic + +/-! # Complete lattices and groups -/ + +variable {α : Type*} {ι : Sort*} {κ : ι → Sort*} + [CompleteLattice α] [Mul α] [MulLeftMono α] [MulRightMono α] + +@[to_additive] +lemma iSup_mul_le (u v : ι → α) : + ⨆ i, u i * v i ≤ (⨆ i, u i) * ⨆ i, v i := + iSup_le fun _ ↦ mul_le_mul' (le_iSup ..) (le_iSup ..) + +@[to_additive] +lemma le_iInf_mul (u v : ι → α) : + (⨅ i, u i) * ⨅ i, v i ≤ ⨅ i, u i * v i := + iSup_mul_le (α := αᵒᵈ) .. + +@[to_additive] +lemma iSup₂_mul_le (u v : (i : ι) → κ i → α) : + ⨆ (i) (j), u i j * v i j ≤ (⨆ (i) (j), u i j) * ⨆ (i) (j), v i j := by + refine le_trans ?_ (iSup_mul_le ..) + gcongr + exact iSup_mul_le .. + +@[to_additive] +lemma le_iInf₂_mul (u v : (i : ι) → κ i → α) : + (⨅ (i) (j), u i j) * ⨅ (i) (j), v i j ≤ ⨅ (i) (j), u i j * v i j := + iSup₂_mul_le (α := αᵒᵈ) .. diff --git a/Mathlib/Order/DirectedInverseSystem.lean b/Mathlib/Order/DirectedInverseSystem.lean index cf03e12e394c71..c675d8319d0847 100644 --- a/Mathlib/Order/DirectedInverseSystem.lean +++ b/Mathlib/Order/DirectedInverseSystem.lean @@ -283,7 +283,6 @@ then `piLT X i` is the limit of all `piLT X j` for `j < i`. -/ @[simps apply] noncomputable def piLTLim : piLT X i ≃ limit (piLTProj (X := X)) i where toFun f := ⟨fun j ↦ piLTProj j.2.le f, fun _ _ _ ↦ rfl⟩ invFun f l := let k := hi.mid l.2; f.1 ⟨k, k.2.2⟩ ⟨l, k.2.1⟩ - left_inv f := rfl right_inv f := by ext j l set k := hi.mid (l.2.trans j.2) diff --git a/Mathlib/Order/Disjoint.lean b/Mathlib/Order/Disjoint.lean index 9de5f19da5beaa..0bc9a8683114c1 100644 --- a/Mathlib/Order/Disjoint.lean +++ b/Mathlib/Order/Disjoint.lean @@ -60,13 +60,13 @@ theorem disjoint_bot_left : Disjoint ⊥ a := fun _ hbot _ ↦ hbot @[simp] theorem disjoint_bot_right : Disjoint a ⊥ := fun _ _ hbot ↦ hbot -theorem Disjoint.mono (h₁ : a ≤ b) (h₂ : c ≤ d) : Disjoint b d → Disjoint a c := +@[gcongr] theorem Disjoint.mono (h₁ : a ≤ b) (h₂ : c ≤ d) : Disjoint b d → Disjoint a c := fun h _ ha hc ↦ h (ha.trans h₁) (hc.trans h₂) -theorem Disjoint.mono_left (h : a ≤ b) : Disjoint b c → Disjoint a c := +@[gcongr] theorem Disjoint.mono_left (h : a ≤ b) : Disjoint b c → Disjoint a c := Disjoint.mono h le_rfl -theorem Disjoint.mono_right : b ≤ c → Disjoint a c → Disjoint a b := +@[gcongr] theorem Disjoint.mono_right : b ≤ c → Disjoint a c → Disjoint a b := Disjoint.mono le_rfl @[simp] @@ -223,13 +223,13 @@ theorem codisjoint_top_left : Codisjoint ⊤ a := fun _ htop _ ↦ htop @[simp] theorem codisjoint_top_right : Codisjoint a ⊤ := fun _ _ htop ↦ htop -theorem Codisjoint.mono (h₁ : a ≤ b) (h₂ : c ≤ d) : Codisjoint a c → Codisjoint b d := +@[gcongr] theorem Codisjoint.mono (h₁ : a ≤ b) (h₂ : c ≤ d) : Codisjoint a c → Codisjoint b d := fun h _ ha hc ↦ h (h₁.trans ha) (h₂.trans hc) -theorem Codisjoint.mono_left (h : a ≤ b) : Codisjoint a c → Codisjoint b c := +@[gcongr] theorem Codisjoint.mono_left (h : a ≤ b) : Codisjoint a c → Codisjoint b c := Codisjoint.mono h le_rfl -theorem Codisjoint.mono_right : b ≤ c → Codisjoint a b → Codisjoint a c := +@[gcongr] theorem Codisjoint.mono_right : b ≤ c → Codisjoint a b → Codisjoint a c := Codisjoint.mono le_rfl @[simp] diff --git a/Mathlib/Order/Filter/Basic.lean b/Mathlib/Order/Filter/Basic.lean index e3474fa5da166f..37840871f6697e 100644 --- a/Mathlib/Order/Filter/Basic.lean +++ b/Mathlib/Order/Filter/Basic.lean @@ -130,9 +130,9 @@ theorem exists_mem_and_iff {P : Set α → Prop} {Q : Set α → Prop} (hP : Ant · rintro ⟨u, huf, hPu, hQu⟩ exact ⟨⟨u, huf, hPu⟩, u, huf, hQu⟩ +@[deprecated forall_swap (since := "2025-06-10")] theorem forall_in_swap {β : Type*} {p : Set α → β → Prop} : - (∀ a ∈ f, ∀ (b), p a b) ↔ ∀ (b), ∀ a ∈ f, p a b := - Set.forall_in_swap + (∀ a ∈ f, ∀ (b), p a b) ↔ ∀ (b), ∀ a ∈ f, p a b := by tauto end Filter @@ -622,6 +622,11 @@ theorem Eventually.mono {p q : α → Prop} {f : Filter α} (hp : ∀ᶠ x in f, (hq : ∀ x, p x → q x) : ∀ᶠ x in f, q x := hp.mp (Eventually.of_forall hq) +@[gcongr] +theorem GCongr.eventually_mono {p q : α → Prop} {f : Filter α} (h : ∀ x, p x → q x) : + (∀ᶠ x in f, p x) → ∀ᶠ x in f, q x := + (·.mono h) + theorem forall_eventually_of_eventually_forall {f : Filter α} {p : α → β → Prop} (h : ∀ᶠ x in f, ∀ y, p x y) : ∀ y, ∀ᶠ x in f, p x y := fun y => h.mono fun _ h => h y @@ -723,6 +728,11 @@ theorem Frequently.mono {p q : α → Prop} {f : Filter α} (h : ∃ᶠ x in f, (hpq : ∀ x, p x → q x) : ∃ᶠ x in f, q x := h.mp (Eventually.of_forall hpq) +@[gcongr] +theorem GCongr.frequently_mono {p q : α → Prop} {f : Filter α} (h : ∀ x, p x → q x) : + (∃ᶠ x in f, p x) → ∃ᶠ x in f, q x := + (·.mono h) + theorem Frequently.and_eventually {p q : α → Prop} {f : Filter α} (hp : ∃ᶠ x in f, p x) (hq : ∀ᶠ x in f, q x) : ∃ᶠ x in f, p x ∧ q x := by refine mt (fun h => hq.mp <| h.mono ?_) hp diff --git a/Mathlib/Order/Filter/Map.lean b/Mathlib/Order/Filter/Map.lean index c5682a4ca38485..0df11b982fdb00 100644 --- a/Mathlib/Order/Filter/Map.lean +++ b/Mathlib/Order/Filter/Map.lean @@ -748,7 +748,7 @@ theorem map_eq_comap_of_inverse {f : Filter α} {m : α → β} {n : β → α} theorem comap_equiv_symm (e : α ≃ β) (f : Filter α) : comap e.symm f = map e f := (map_eq_comap_of_inverse e.self_comp_symm e.symm_comp_self).symm -theorem map_swap_eq_comap_swap {f : Filter (α × β)} : Prod.swap <$> f = comap Prod.swap f := +theorem map_swap_eq_comap_swap {f : Filter (α × β)} : map Prod.swap f = comap Prod.swap f := map_eq_comap_of_inverse Prod.swap_swap_eq Prod.swap_swap_eq /-- A useful lemma when dealing with uniformities. -/ diff --git a/Mathlib/Order/Hom/Basic.lean b/Mathlib/Order/Hom/Basic.lean index 0949b2d9a23bcc..c727875aa52f18 100644 --- a/Mathlib/Order/Hom/Basic.lean +++ b/Mathlib/Order/Hom/Basic.lean @@ -17,13 +17,13 @@ homomorphism `f : α →o β` is a function `α → β` along with a proof that In this file we define the following bundled monotone maps: * `OrderHom α β` a.k.a. `α →o β`: Preorder homomorphism. - An `OrderHom α β` is a function `f : α → β` such that `a₁ ≤ a₂ → f a₁ ≤ f a₂` + An `OrderHom α β` is a function `f : α → β` such that `a₁ ≤ a₂ → f a₁ ≤ f a₂` * `OrderEmbedding α β` a.k.a. `α ↪o β`: Relation embedding. - An `OrderEmbedding α β` is an embedding `f : α ↪ β` such that `a ≤ b ↔ f a ≤ f b`. - Defined as an abbreviation of `@RelEmbedding α β (≤) (≤)`. + An `OrderEmbedding α β` is an embedding `f : α ↪ β` such that `a ≤ b ↔ f a ≤ f b`. + Defined as an abbreviation of `@RelEmbedding α β (≤) (≤)`. * `OrderIso`: Relation isomorphism. - An `OrderIso α β` is an equivalence `f : α ≃ β` such that `a ≤ b ↔ f a ≤ f b`. - Defined as an abbreviation of `@RelIso α β (≤) (≤)`. + An `OrderIso α β` is an equivalence `f : α ≃ β` such that `a ≤ b ↔ f a ≤ f b`. + Defined as an abbreviation of `@RelIso α β (≤) (≤)`. We also define many `OrderHom`s. In some cases we define two versions, one with `ₘ` suffix and one without it (e.g., `OrderHom.compₘ` and `OrderHom.comp`). This means that the former @@ -296,8 +296,6 @@ def curry : (α × β →o γ) ≃o (α →o β →o γ) where toFun f := ⟨fun x ↦ ⟨Function.curry f x, fun _ _ h ↦ f.mono ⟨le_rfl, h⟩⟩, fun _ _ h _ => f.mono ⟨h, le_rfl⟩⟩ invFun f := ⟨Function.uncurry fun x ↦ f x, fun x y h ↦ (f.mono h.1 x.2).trans ((f y.1).mono h.2)⟩ - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := by simp [le_def] @[simp] @@ -409,8 +407,6 @@ of monotone maps to `β` and `γ`. -/ def prodIso : (α →o β × γ) ≃o (α →o β) × (α →o γ) where toFun f := (fst.comp f, snd.comp f) invFun f := f.1.prod f.2 - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := forall_and.symm /-- `Prod.map` of two `OrderHom`s as an `OrderHom` -/ @@ -450,8 +446,6 @@ maps `Π i, α →o π i`. -/ def piIso : (α →o ∀ i, π i) ≃o ∀ i, α →o π i where toFun f i := (Pi.evalOrderHom i).comp f invFun := pi - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := forall_swap /-- `Subtype.val` as a bundled monotone function. -/ @@ -480,8 +474,6 @@ protected def dual : (α →o β) ≃ (αᵒᵈ →o βᵒᵈ) where toFun f := ⟨(OrderDual.toDual : β → βᵒᵈ) ∘ (f : α → β) ∘ (OrderDual.ofDual : αᵒᵈ → α), f.mono.dual⟩ invFun f := ⟨OrderDual.ofDual ∘ f ∘ OrderDual.toDual, f.mono.dual⟩ - left_inv _ := rfl - right_inv _ := rfl @[simp] theorem dual_id : (OrderHom.id : α →o α).dual = OrderHom.id := @@ -784,9 +776,15 @@ theorem symm_injective : Function.Injective (symm : α ≃o β → β ≃o α) : symm_bijective.injective @[simp] -theorem toEquiv_symm (e : α ≃o β) : e.toEquiv.symm = e.symm.toEquiv := +theorem toEquiv_symm (e : α ≃o β) : e.symm.toEquiv = e.toEquiv.symm := rfl +@[simp] +theorem coe_toEquiv (e : α ≃o β) : ⇑e.toEquiv = e := rfl + +@[simp] +theorem coe_symm_toEquiv (e : α ≃o β) : ⇑e.toEquiv.symm = e.symm := rfl + /-- Composition of two order isomorphisms is an order isomorphism. -/ @[trans] def trans (e : α ≃o β) (e' : β ≃o γ) : α ≃o γ := @@ -939,9 +937,15 @@ theorem toRelIsoLT_apply (e : α ≃o β) (x : α) : e.toRelIsoLT x = e x := rfl @[simp] -theorem toRelIsoLT_symm (e : α ≃o β) : e.toRelIsoLT.symm = e.symm.toRelIsoLT := +theorem toRelIsoLT_symm (e : α ≃o β) : e.symm.toRelIsoLT = e.toRelIsoLT.symm := rfl +@[simp] +theorem coe_toRelIsoLT (e : α ≃o β) : ⇑e.toRelIsoLT = e := rfl + +@[simp] +theorem coe_symm_toRelIsoLT (e : α ≃o β) : ⇑e.toRelIsoLT.symm = e.symm := rfl + /-- Converts a `RelIso (<) (<)` into an `OrderIso`. -/ def ofRelIsoLT {α β} [PartialOrder α] [PartialOrder β] (e : ((· < ·) : α → α → Prop) ≃r ((· < ·) : β → β → Prop)) : α ≃o β := diff --git a/Mathlib/Order/Hom/Bounded.lean b/Mathlib/Order/Hom/Bounded.lean index 504a97edc5466a..541f7650316069 100644 --- a/Mathlib/Order/Hom/Bounded.lean +++ b/Mathlib/Order/Hom/Bounded.lean @@ -648,8 +648,6 @@ protected def dual : TopHom α β ≃ BotHom αᵒᵈ βᵒᵈ where toFun f := ⟨f, f.map_top'⟩ invFun f := ⟨f, f.map_bot'⟩ - left_inv _ := TopHom.ext fun _ => rfl - right_inv _ := BotHom.ext fun _ => rfl @[simp] theorem dual_id : TopHom.dual (TopHom.id α) = BotHom.id _ := @@ -681,8 +679,6 @@ protected def dual : BotHom α β ≃ TopHom αᵒᵈ βᵒᵈ where toFun f := ⟨f, f.map_bot'⟩ invFun f := ⟨f, f.map_top'⟩ - left_inv _ := BotHom.ext fun _ => rfl - right_inv _ := TopHom.ext fun _ => rfl @[simp] theorem dual_id : BotHom.dual (BotHom.id α) = TopHom.id _ := @@ -717,8 +713,6 @@ protected def dual : βᵒᵈ where toFun f := ⟨f.toOrderHom.dual, f.map_bot', f.map_top'⟩ invFun f := ⟨OrderHom.dual.symm f.toOrderHom, f.map_bot', f.map_top'⟩ - left_inv _ := ext fun _ => rfl - right_inv _ := ext fun _ => rfl @[simp] theorem dual_id : (BoundedOrderHom.id α).dual = BoundedOrderHom.id _ := diff --git a/Mathlib/Order/Hom/BoundedLattice.lean b/Mathlib/Order/Hom/BoundedLattice.lean index 53e35c7d1c1693..73940ce1cccd0f 100644 --- a/Mathlib/Order/Hom/BoundedLattice.lean +++ b/Mathlib/Order/Hom/BoundedLattice.lean @@ -680,8 +680,6 @@ lattices. -/ def dual : SupBotHom α β ≃ InfTopHom αᵒᵈ βᵒᵈ where toFun f := ⟨SupHom.dual f.toSupHom, f.map_bot'⟩ invFun f := ⟨SupHom.dual.symm f.toInfHom, f.map_top'⟩ - left_inv _ := rfl - right_inv _ := rfl @[simp] theorem dual_id : SupBotHom.dual (SupBotHom.id α) = InfTopHom.id _ := rfl @@ -712,8 +710,6 @@ lattices. -/ protected def dual : InfTopHom α β ≃ SupBotHom αᵒᵈ βᵒᵈ where toFun f := ⟨InfHom.dual f.toInfHom, f.map_top'⟩ invFun f := ⟨InfHom.dual.symm f.toSupHom, f.map_bot'⟩ - left_inv _ := rfl - right_inv _ := rfl @[simp] theorem dual_id : InfTopHom.dual (InfTopHom.id α) = SupBotHom.id _ := @@ -746,8 +742,6 @@ bounded lattices. -/ protected def dual : BoundedLatticeHom α β ≃ BoundedLatticeHom αᵒᵈ βᵒᵈ where toFun f := ⟨LatticeHom.dual f.toLatticeHom, f.map_bot', f.map_top'⟩ invFun f := ⟨LatticeHom.dual.symm f.toLatticeHom, f.map_bot', f.map_top'⟩ - left_inv _ := rfl - right_inv _ := rfl @[simp] theorem dual_id : BoundedLatticeHom.dual (BoundedLatticeHom.id α) = BoundedLatticeHom.id _ := diff --git a/Mathlib/Order/Hom/CompleteLattice.lean b/Mathlib/Order/Hom/CompleteLattice.lean index c7d329a89a5988..99285890df738c 100644 --- a/Mathlib/Order/Hom/CompleteLattice.lean +++ b/Mathlib/Order/Hom/CompleteLattice.lean @@ -681,8 +681,6 @@ variable [SupSet α] [SupSet β] [SupSet γ] protected def dual : sSupHom α β ≃ sInfHom αᵒᵈ βᵒᵈ where toFun f := ⟨toDual ∘ f ∘ ofDual, f.map_sSup'⟩ invFun f := ⟨ofDual ∘ f ∘ toDual, f.map_sInf'⟩ - left_inv _ := sSupHom.ext fun _ => rfl - right_inv _ := sInfHom.ext fun _ => rfl @[simp] theorem dual_id : sSupHom.dual (sSupHom.id α) = sInfHom.id _ := @@ -717,8 +715,6 @@ protected def dual : sInfHom α β ≃ sSupHom αᵒᵈ βᵒᵈ where invFun f := { toFun := ofDual ∘ f ∘ toDual map_sInf' := fun _ => congr_arg ofDual (map_sSup f _) } - left_inv _ := sInfHom.ext fun _ => rfl - right_inv _ := sSupHom.ext fun _ => rfl @[simp] theorem dual_id : sInfHom.dual (sInfHom.id α) = sSupHom.id _ := @@ -750,8 +746,6 @@ lattices. -/ protected def dual : CompleteLatticeHom α β ≃ CompleteLatticeHom αᵒᵈ βᵒᵈ where toFun f := ⟨sSupHom.dual f.tosSupHom, fun s ↦ f.map_sInf' s⟩ invFun f := ⟨sSupHom.dual f.tosSupHom, fun s ↦ f.map_sInf' s⟩ - left_inv _ := ext fun _ => rfl - right_inv _ := ext fun _ => rfl @[simp] theorem dual_id : CompleteLatticeHom.dual (CompleteLatticeHom.id α) = CompleteLatticeHom.id _ := diff --git a/Mathlib/Order/Hom/Lattice.lean b/Mathlib/Order/Hom/Lattice.lean index f3ff520bae285b..37b7cc655f96f8 100644 --- a/Mathlib/Order/Hom/Lattice.lean +++ b/Mathlib/Order/Hom/Lattice.lean @@ -706,8 +706,6 @@ variable [Max α] [Max β] [Max γ] protected def dual : SupHom α β ≃ InfHom αᵒᵈ βᵒᵈ where toFun f := ⟨f, f.map_sup'⟩ invFun f := ⟨f, f.map_inf'⟩ - left_inv _ := rfl - right_inv _ := rfl @[simp] theorem dual_id : SupHom.dual (SupHom.id α) = InfHom.id _ := @@ -739,8 +737,6 @@ variable [Min α] [Min β] [Min γ] protected def dual : InfHom α β ≃ SupHom αᵒᵈ βᵒᵈ where toFun f := ⟨f, f.map_inf'⟩ invFun f := ⟨f, f.map_sup'⟩ - left_inv _ := rfl - right_inv _ := rfl @[simp] theorem dual_id : InfHom.dual (InfHom.id α) = SupHom.id _ := @@ -772,8 +768,6 @@ variable [Lattice α] [Lattice β] [Lattice γ] protected def dual : LatticeHom α β ≃ LatticeHom αᵒᵈ βᵒᵈ where toFun f := ⟨InfHom.dual f.toInfHom, f.map_sup'⟩ invFun f := ⟨SupHom.dual.symm f.toInfHom, f.map_sup'⟩ - left_inv _ := rfl - right_inv _ := rfl @[simp] theorem dual_id : LatticeHom.dual (LatticeHom.id α) = LatticeHom.id _ := rfl diff --git a/Mathlib/Order/Hom/WithTopBot.lean b/Mathlib/Order/Hom/WithTopBot.lean index 0789a88cf982ba..606948be65f2d7 100644 --- a/Mathlib/Order/Hom/WithTopBot.lean +++ b/Mathlib/Order/Hom/WithTopBot.lean @@ -171,12 +171,12 @@ theorem withTopCongr_refl : (OrderIso.refl α).withTopCongr = OrderIso.refl _ := RelIso.toEquiv_injective Equiv.optionCongr_refl @[simp] -theorem withTopCongr_symm (e : α ≃o β) : e.withTopCongr.symm = e.symm.withTopCongr := +theorem withTopCongr_symm (e : α ≃o β) : e.symm.withTopCongr = e.withTopCongr.symm := RelIso.toEquiv_injective e.toEquiv.optionCongr_symm @[simp] theorem withTopCongr_trans (e₁ : α ≃o β) (e₂ : β ≃o γ) : - e₁.withTopCongr.trans e₂.withTopCongr = (e₁.trans e₂).withTopCongr := + (e₁.trans e₂).withTopCongr = e₁.withTopCongr.trans e₂.withTopCongr := RelIso.toEquiv_injective <| e₁.toEquiv.optionCongr_trans e₂.toEquiv /-- A version of `Equiv.optionCongr` for `WithBot`. -/ @@ -189,12 +189,12 @@ theorem withBotCongr_refl : (OrderIso.refl α).withBotCongr = OrderIso.refl _ := RelIso.toEquiv_injective Equiv.optionCongr_refl @[simp] -theorem withBotCongr_symm (e : α ≃o β) : e.withBotCongr.symm = e.symm.withBotCongr := +theorem withBotCongr_symm (e : α ≃o β) : e.symm.withBotCongr = e.withBotCongr.symm := RelIso.toEquiv_injective e.toEquiv.optionCongr_symm @[simp] theorem withBotCongr_trans (e₁ : α ≃o β) (e₂ : β ≃o γ) : - e₁.withBotCongr.trans e₂.withBotCongr = (e₁.trans e₂).withBotCongr := + (e₁.trans e₂).withBotCongr = e₁.withBotCongr.trans e₂.withBotCongr := RelIso.toEquiv_injective <| e₁.toEquiv.optionCongr_trans e₂.toEquiv end OrderIso diff --git a/Mathlib/Order/Interval/Basic.lean b/Mathlib/Order/Interval/Basic.lean index bfaf3479a3ec24..3fb4bbd523bf87 100644 --- a/Mathlib/Order/Interval/Basic.lean +++ b/Mathlib/Order/Interval/Basic.lean @@ -76,8 +76,6 @@ def toDualProdHom : NonemptyInterval α ↪o αᵒᵈ × α where def dual : NonemptyInterval α ≃ NonemptyInterval αᵒᵈ where toFun s := ⟨s.toProd.swap, s.fst_le_snd⟩ invFun s := ⟨s.toProd.swap, s.fst_le_snd⟩ - left_inv _ := rfl - right_inv _ := rfl @[simp] theorem fst_dual (s : NonemptyInterval α) : s.dual.fst = toDual s.snd := diff --git a/Mathlib/Order/Interval/Finset/Basic.lean b/Mathlib/Order/Interval/Finset/Basic.lean index d419b814b0313c..fc405fa5742cdb 100644 --- a/Mathlib/Order/Interval/Finset/Basic.lean +++ b/Mathlib/Order/Interval/Finset/Basic.lean @@ -471,8 +471,6 @@ theorem Iic_disjoint_Ioc (h : a ≤ b) : Disjoint (Iic a) (Ioc b c) := def _root_.Equiv.IicFinsetSet (a : α) : Iic a ≃ Set.Iic a where toFun b := ⟨b.1, coe_Iic a ▸ mem_coe.2 b.2⟩ invFun b := ⟨b.1, by rw [← mem_coe, coe_Iic a]; exact b.2⟩ - left_inv := fun _ ↦ rfl - right_inv := fun _ ↦ rfl end LocallyFiniteOrderBot diff --git a/Mathlib/Order/Lattice.lean b/Mathlib/Order/Lattice.lean index 7be11958ffd007..a63a2cea36fed1 100644 --- a/Mathlib/Order/Lattice.lean +++ b/Mathlib/Order/Lattice.lean @@ -6,7 +6,6 @@ Authors: Johannes Hölzl import Mathlib.Data.Bool.Basic import Mathlib.Order.Monotone.Basic import Mathlib.Order.ULift -import Mathlib.Tactic.GCongr.CoreAttrs /-! # (Semi-)lattices @@ -49,10 +48,6 @@ semilattice, lattice -/ -/-- See if the term is `a ⊂ b` and the goal is `a ⊆ b`. -/ -@[gcongr_forward] def exactSubsetOfSSubset : Mathlib.Tactic.GCongr.ForwardExt where - eval h goal := do goal.assignIfDefEq (← Lean.Meta.mkAppM ``subset_of_ssubset #[h]) - universe u v w variable {α : Type u} {β : Type v} diff --git a/Mathlib/Order/RelClasses.lean b/Mathlib/Order/RelClasses.lean index 850f8f3056f3b3..70b320d4e97c0d 100644 --- a/Mathlib/Order/RelClasses.lean +++ b/Mathlib/Order/RelClasses.lean @@ -11,7 +11,8 @@ import Batteries.WF /-! # Unbundled relation classes -In this file we prove some properties of `Is*` classes defined in `Mathlib/Order/Defs.lean`. +In this file we prove some properties of `Is*` classes defined in +`Mathlib/Order/Defs/Unbundled.lean`. The main difference between these classes and the usual order classes (`Preorder` etc) is that usual classes extend `LE` and/or `LT` while these classes take a relation as an explicit argument. -/ @@ -639,6 +640,38 @@ theorem subset_iff_ssubset_or_eq [IsRefl α (· ⊆ ·)] [IsAntisymm α (· ⊆ a ⊆ b ↔ a ⊂ b ∨ a = b := ⟨fun h => h.ssubset_or_eq, fun h => h.elim subset_of_ssubset subset_of_eq⟩ +namespace GCongr + +variable [IsTrans α (· ⊆ ·)] {a b c d : α} + +@[gcongr] +theorem ssubset_imp_ssubset (h₁ : c ⊆ a) (h₂ : b ⊆ d) : a ⊂ b → c ⊂ d := + fun h => (h₁.trans_ssubset h).trans_subset h₂ + +attribute [gcongr] ssubset_of_subset_of_ssubset + +@[gcongr] +theorem ssubset_imp_ssubset_right (h₁ : a ⊆ b) : c ⊂ a → c ⊂ b := + fun h₂ => (h₂.subset.trans h₁).ssubset_of_not_subset fun h => h₂.not_subset (h₁.trans h) + +@[gcongr] +theorem ssuperset_imp_ssuperset (h₁ : a ⊆ c) (h₂ : d ⊆ b) : a ⊃ b → c ⊃ d := + ssubset_imp_ssubset h₂ h₁ + +@[gcongr] +theorem ssuperset_imp_ssuperset_left (h : a ⊆ b) : c ⊃ b → c ⊃ a := + ssubset_of_subset_of_ssubset h + +@[gcongr] +theorem ssuperset_imp_ssuperset_right (h : a ⊆ b) : a ⊃ c → b ⊃ c := + ssubset_imp_ssubset_right h + +/-- See if the term is `a ⊂ b` and the goal is `a ⊆ b`. -/ +@[gcongr_forward] def exactSubsetOfSSubset : Mathlib.Tactic.GCongr.ForwardExt where + eval h goal := do goal.assignIfDefEq (← Lean.Meta.mkAppM ``subset_of_ssubset #[h]) + +end GCongr + end SubsetSsubset /-! ### Conversion of bundled order typeclasses to unbundled relation typeclasses -/ diff --git a/Mathlib/Probability/Distributions/Gaussian/Basic.lean b/Mathlib/Probability/Distributions/Gaussian/Basic.lean index 91f547bee84e82..ce1d7efd6c9a88 100644 --- a/Mathlib/Probability/Distributions/Gaussian/Basic.lean +++ b/Mathlib/Probability/Distributions/Gaussian/Basic.lean @@ -153,4 +153,31 @@ instance isGaussian_conv [SecondCountableTopology E] IsGaussian.map_eq_gaussianReal L, gaussianReal_conv_gaussianReal] congr <;> simp [variance_nonneg] +instance (c : E) : IsGaussian (μ.map (fun x ↦ x + c)) := by + refine isGaussian_of_charFunDual_eq fun L ↦ ?_ + rw [charFunDual_map_add_const, IsGaussian.charFunDual_eq, ← exp_add] + have hL_comp : L ∘ (fun x ↦ x + c) = fun x ↦ L x + L c := by ext; simp + rw [variance_map (by fun_prop) (by fun_prop), integral_map (by fun_prop) (by fun_prop), + hL_comp, variance_add_const (by fun_prop), integral_complex_ofReal, integral_complex_ofReal] + simp only [map_add, ofReal_add] + rw [integral_add (by fun_prop) (by fun_prop)] + congr + simp only [integral_const, measureReal_univ_eq_one, smul_eq_mul, one_mul, ofReal_add] + ring + +instance (c : E) : IsGaussian (μ.map (fun x ↦ c + x)) := by simp_rw [add_comm c]; infer_instance + +instance (c : E) : IsGaussian (μ.map (fun x ↦ x - c)) := by simp_rw [sub_eq_add_neg]; infer_instance + +instance : IsGaussian (μ.map (fun x ↦ -x)) := by + change IsGaussian (μ.map (ContinuousLinearEquiv.neg ℝ)) + infer_instance + +instance (c : E) : IsGaussian (μ.map (fun x ↦ c - x)) := by + simp_rw [sub_eq_add_neg] + suffices IsGaussian ((μ.map (fun x ↦ -x)).map (fun x ↦ c + x)) by + rw [Measure.map_map (by fun_prop) (by fun_prop)] at this + convert this using 1 + infer_instance + end ProbabilityTheory diff --git a/Mathlib/Probability/ProductMeasure.lean b/Mathlib/Probability/ProductMeasure.lean index 19d75f0a6e18d7..5c606b3dea3b39 100644 --- a/Mathlib/Probability/ProductMeasure.lean +++ b/Mathlib/Probability/ProductMeasure.lean @@ -81,9 +81,7 @@ theorem piContent_eq_measure_pi [Fintype ι] {s : Set (Π i, X i)} (hs : Measura piContent μ s = Measure.pi μ s := by let e : @Finset.univ ι _ ≃ ι := { toFun i := i - invFun i := ⟨i, mem_univ i⟩ - left_inv := fun _ ↦ rfl - right_inv := fun _ ↦ rfl } + invFun i := ⟨i, mem_univ i⟩ } have : s = cylinder univ (MeasurableEquiv.piCongrLeft X e ⁻¹' s) := rfl nth_rw 1 [this] rw [piContent_cylinder _ (hs.preimage (by fun_prop)), ← Measure.pi_map_piCongrLeft e, diff --git a/Mathlib/RepresentationTheory/FDRep.lean b/Mathlib/RepresentationTheory/FDRep.lean index 7db74bd2d52ce7..398d3abe188242 100644 --- a/Mathlib/RepresentationTheory/FDRep.lean +++ b/Mathlib/RepresentationTheory/FDRep.lean @@ -176,8 +176,6 @@ def forget₂HomLinearEquiv (X Y : FDRep R G) : map_add' _ _ := rfl map_smul' _ _ := rfl invFun f := ⟨(forget₂ (FGModuleCat R) (ModuleCat R)).map f.hom, f.comm⟩ - left_inv _ := by ext; rfl - right_inv _ := by ext; rfl end FDRep diff --git a/Mathlib/RepresentationTheory/GroupCohomology/Basic.lean b/Mathlib/RepresentationTheory/GroupCohomology/Basic.lean index a0e442d0e6042b..03cf5f1cc33f86 100644 --- a/Mathlib/RepresentationTheory/GroupCohomology/Basic.lean +++ b/Mathlib/RepresentationTheory/GroupCohomology/Basic.lean @@ -4,8 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Amelia Livingston -/ import Mathlib.Algebra.Homology.Opposite -import Mathlib.Algebra.Homology.ShortComplex.HomologicalComplex import Mathlib.RepresentationTheory.GroupCohomology.Resolution +import Mathlib.Tactic.CategoryTheory.Slice /-! # The group cohomology of a `k`-linear `G`-representation @@ -18,12 +18,12 @@ $$\rho(g_0)(f(g_1, \dots, g_n))$$ $$+ \sum_{i = 0}^{n - 1} (-1)^{i + 1}\cdot f(g_0, \dots, g_ig_{i + 1}, \dots, g_n)$$ $$+ (-1)^{n + 1}\cdot f(g_0, \dots, g_{n - 1})$$ (where `ρ` is the representation attached to `A`). -We have a `k`-linear isomorphism $\mathrm{Fun}(G^n, A) \cong \mathrm{Hom}(k[G^{n + 1}], A)$, where -the righthand side is morphisms in `Rep k G`, and the representation on $k[G^{n + 1}]$ -is induced by the diagonal action of `G`. If we conjugate the $n$th differential in -$\mathrm{Hom}(P, A)$ by this isomorphism, where `P` is the standard resolution of `k` as a trivial -`k`-linear `G`-representation, then the resulting map agrees with the differential $d^n$ defined -above, a fact we prove. +We have a `k`-linear isomorphism +$\mathrm{Fun}(G^n, A) \cong \mathrm{Hom}(\bigoplus_{G^n} k[G], A)$, where +the righthand side is morphisms in `Rep k G`, and $k[G]$ is equipped with the left regular +representation. If we conjugate the $n$th differential in $\mathrm{Hom}(P, A)$ by this isomorphism, +where `P` is the bar resolution of `k` as a trivial `k`-linear `G`-representation, then the +resulting map agrees with the differential $d^n$ defined above, a fact we prove. This gives us for free a proof that our $d^n$ squares to zero. It also gives us an isomorphism $\mathrm{H}^n(G, A) \cong \mathrm{Ext}^n(k, A),$ where $\mathrm{Ext}$ is taken in the category @@ -35,14 +35,12 @@ To talk about cohomology in low degree, please see the file ## Main definitions -* `groupCohomology.linearYonedaObjResolution A`: a complex whose objects are the representation - morphisms $\mathrm{Hom}(k[G^{n + 1}], A)$ and whose cohomology is the group cohomology - $\mathrm{H}^n(G, A)$. * `groupCohomology.inhomogeneousCochains A`: a complex whose objects are $\mathrm{Fun}(G^n, A)$ and whose cohomology is the group cohomology $\mathrm{H}^n(G, A).$ -* `groupCohomology.inhomogeneousCochainsIso A`: an isomorphism between the above two complexes. -* `groupCohomology A n`: this is $\mathrm{H}^n(G, A),$ defined as the $n$th cohomology of the - second complex, `inhomogeneousCochains A`. +* `groupCohomology.inhomogeneousCochainsIso A`: an isomorphism between the above complex and the + complex $\mathrm{Hom}(P, A),$ where `P` is the bar resolution of `k` as a trivial resolution. +* `groupCohomology A n`: this is $\mathrm{H}^n(G, A),$ defined as the $n$th cohomology of + `inhomogeneousCochains A`. * `groupCohomologyIsoExt A n`: an isomorphism $\mathrm{H}^n(G, A) \cong \mathrm{Ext}^n(k, A)$ (where $\mathrm{Ext}$ is taken in the category `Rep k G`) induced by `inhomogeneousCochainsIso A`. @@ -82,14 +80,11 @@ variable [Monoid G] /-- The complex `Hom(P, A)`, where `P` is the standard resolution of `k` as a trivial `k`-linear `G`-representation. -/ +@[deprecated "We now use `(Rep.barComplex k G).linearYonedaObj k A instead" + (since := "2025-06-08")] abbrev linearYonedaObjResolution (A : Rep k G) : CochainComplex (ModuleCat.{u} k) ℕ := (Rep.standardComplex k G).linearYonedaObj k A -theorem linearYonedaObjResolution_d_apply - {A : Rep k G} (i j : ℕ) (x : (Rep.standardComplex k G).X i ⟶ A) : - (linearYonedaObjResolution A).d i j x = (Rep.standardComplex k G).d j i ≫ x := - rfl - end groupCohomology namespace inhomogeneousCochains @@ -98,138 +93,83 @@ open Rep groupCohomology /-- The differential in the complex of inhomogeneous cochains used to calculate group cohomology. -/ -@[simps] -def d [Monoid G] (n : ℕ) (A : Rep k G) : ((Fin n → G) → A) →ₗ[k] (Fin (n + 1) → G) → A where - toFun f g := - A.ρ (g 0) (f fun i => g i.succ) + - Finset.univ.sum fun j : Fin (n + 1) => +@[simps! -isSimp] +def d [Monoid G] (A : Rep k G) (n : ℕ) : + ModuleCat.of k ((Fin n → G) → A) ⟶ ModuleCat.of k ((Fin (n + 1) → G) → A) := + ModuleCat.ofHom + { toFun f g := + A.ρ (g 0) (f fun i => g i.succ) + Finset.univ.sum fun j : Fin (n + 1) => (-1 : k) ^ ((j : ℕ) + 1) • f (Fin.contractNth j (· * ·) g) - map_add' f g := by - ext x -/- Porting note: changed from `simp only` which needed extra heartbeats -/ - simp_rw [Pi.add_apply, map_add, smul_add, Finset.sum_add_distrib, add_add_add_comm] - map_smul' r f := by - ext x -/- Porting note: changed from `simp only` which needed extra heartbeats -/ - simp_rw [Pi.smul_apply, RingHom.id_apply, map_smul, smul_add, Finset.smul_sum, ← smul_assoc, - smul_eq_mul, mul_comm r] - -variable [Group G] (n) (A : Rep k G) - -/-- The theorem that our isomorphism `Fun(Gⁿ, A) ≅ Hom(k[Gⁿ⁺¹], A)` (where the righthand side is -morphisms in `Rep k G`) commutes with the differentials in the complex of inhomogeneous cochains -and the homogeneous `linearYonedaObjResolution`. -/ + map_add' f g := by + ext + simp [Finset.sum_add_distrib, add_add_add_comm] + map_smul' r f := by + ext + simp [Finset.smul_sum, ← smul_assoc, mul_comm r] } + +variable [Group G] [DecidableEq G] (A : Rep k G) (n : ℕ) + theorem d_eq : - d n A = - ((diagonalHomEquiv n A).toModuleIso.inv ≫ - (linearYonedaObjResolution A).d n (n + 1) ≫ - (diagonalHomEquiv (n + 1) A).toModuleIso.hom).hom := by - ext f g -/- Porting note (https://github.com/leanprover-community/mathlib4/issues/11039): broken proof was - simp only [ModuleCat.coe_comp, LinearEquiv.coe_coe, Function.comp_apply, - LinearEquiv.toModuleIso_inv, linearYonedaObjResolution_d_apply, LinearEquiv.toModuleIso_hom, - diagonalHomEquiv_apply, Action.comp_hom, Resolution.d_eq k G n, - Resolution.d_of (Fin.partialProd g), LinearMap.map_sum, - ← Finsupp.smul_single_one _ ((-1 : k) ^ _), map_smul, d_apply] - simp only [@Fin.sum_univ_succ _ _ (n + 1), Fin.val_zero, pow_zero, one_smul, Fin.succAbove_zero, - diagonalHomEquiv_symm_apply f (Fin.partialProd g ∘ @Fin.succ (n + 1)), Function.comp_apply, - Fin.partialProd_succ, Fin.castSucc_zero, Fin.partialProd_zero, one_mul] - congr 1 - · congr - ext - have := Fin.partialProd_right_inv g (Fin.castSucc x) - simp only [mul_inv_rev, Fin.castSucc_fin_succ] at * - rw [mul_assoc, ← mul_assoc _ _ (g x.succ), this, inv_mul_cancel_left] - · exact Finset.sum_congr rfl fun j hj => by - rw [diagonalHomEquiv_symm_partialProd_succ, Fin.val_succ] -/ - -- https://github.com/leanprover-community/mathlib4/issues/5026 - -- https://github.com/leanprover-community/mathlib4/issues/5164 - change d n A f g = diagonalHomEquiv (n + 1) A - ((standardComplex k G).d (n + 1) n ≫ (diagonalHomEquiv n A).symm f) g - rw [diagonalHomEquiv_apply, Action.comp_hom, ConcreteCategory.comp_apply, standardComplex.d_eq] - erw [standardComplex.d_of (Fin.partialProd g)] - simp only [map_sum, ← Finsupp.smul_single_one _ ((-1 : k) ^ _)] - -- This used to be `rw`, but we need `erw` after https://github.com/leanprover/lean4/pull/2644 - erw [d_apply, @Fin.sum_univ_succ _ _ (n + 1), Fin.val_zero, pow_zero, one_smul, - Fin.succAbove_zero, diagonalHomEquiv_symm_apply f (Fin.partialProd g ∘ @Fin.succ (n + 1))] - simp_rw [Function.comp_apply, Fin.partialProd_succ, Fin.castSucc_zero, - Fin.partialProd_zero, one_mul] - rcongr x - · have := Fin.partialProd_right_inv g (Fin.castSucc x) - simp only [mul_inv_rev, Fin.castSucc_fin_succ] at this ⊢ - rw [mul_assoc, ← mul_assoc _ _ (g x.succ), this, inv_mul_cancel_left] - · -- This used to be `rw`, but we need `erw` after https://github.com/leanprover/lean4/pull/2644 - erw [map_smul, diagonalHomEquiv_symm_partialProd_succ, Fin.val_succ] + d A n = + (freeLiftLEquiv (Fin n → G) A).toModuleIso.inv ≫ + ((barComplex k G).linearYonedaObj k A).d n (n + 1) ≫ + (freeLiftLEquiv (Fin (n + 1) → G) A).toModuleIso.hom := by + ext + simp [d_hom_apply, map_add, barComplex.d_single (k := k)] end inhomogeneousCochains namespace groupCohomology -variable [Group G] (n) (A : Rep k G) +variable [Group G] [DecidableEq G] (n) (A : Rep k G) -open inhomogeneousCochains +open inhomogeneousCochains Rep /-- Given a `k`-linear `G`-representation `A`, this is the complex of inhomogeneous cochains $$0 \to \mathrm{Fun}(G^0, A) \to \mathrm{Fun}(G^1, A) \to \mathrm{Fun}(G^2, A) \to \dots$$ which calculates the group cohomology of `A`. -/ noncomputable abbrev inhomogeneousCochains : CochainComplex (ModuleCat k) ℕ := CochainComplex.of (fun n => ModuleCat.of k ((Fin n → G) → A)) - (fun n => ModuleCat.ofHom (inhomogeneousCochains.d n A)) fun n => by -/- Porting note (https://github.com/leanprover-community/mathlib4/issues/11039): broken proof was - ext x y - have := LinearMap.ext_iff.1 ((linearYonedaObjResolution A).d_comp_d n (n + 1) (n + 2)) - simp only [ModuleCat.coe_comp, Function.comp_apply] at this - simp only [ModuleCat.coe_comp, Function.comp_apply, d_eq, LinearEquiv.toModuleIso_hom, - LinearEquiv.toModuleIso_inv, LinearEquiv.coe_coe, LinearEquiv.symm_apply_apply, this, - LinearMap.zero_apply, map_zero, Pi.zero_apply] -/ - ext x - have : ∀ x, _ = (0 : _ →ₗ[_] _) x := LinearMap.ext_iff.1 (ModuleCat.hom_ext_iff.mp - ((linearYonedaObjResolution A).d_comp_d n (n + 1) (n + 2))) - simp only [ModuleCat.hom_comp, LinearMap.comp_apply] at this - dsimp only - simp only [d_eq, LinearEquiv.toModuleIso_inv, LinearEquiv.toModuleIso_hom, - ModuleCat.hom_comp, LinearMap.comp_apply, LinearEquiv.coe_coe, ModuleCat.hom_zero, - ModuleCat.ofHom_comp] - /- Porting note: I can see I need to rewrite `LinearEquiv.coe_coe` twice to at - least reduce the need for `symm_apply_apply` to be an `erw`. However, even `erw` refuses to - rewrite the second `coe_coe`... -/ - erw [LinearEquiv.symm_apply_apply, this] - simp only [LinearMap.zero_apply, ChainComplex.linearYonedaObj_X, linearYoneda_obj_obj_carrier, - map_zero, Pi.zero_apply, LinearMap.zero_apply] - rfl + (fun n => inhomogeneousCochains.d A n) fun n => by + simp only [d_eq] + slice_lhs 3 4 => { rw [Iso.hom_inv_id] } + slice_lhs 2 4 => { rw [Category.id_comp, ((barComplex k G).linearYonedaObj k A).d_comp_d] } + simp + +variable {A n} in +@[ext] +theorem inhomogeneousCochains.ext {x y : (inhomogeneousCochains A).X n} (h : ∀ g, x g = y g) : + x = y := funext h theorem inhomogeneousCochains.d_def (n : ℕ) : - (inhomogeneousCochains A).d n (n + 1) = ModuleCat.ofHom (inhomogeneousCochains.d n A) := - CochainComplex.of_d _ _ _ _ + (inhomogeneousCochains A).d n (n + 1) = d A n := by + simp + +theorem inhomogeneousCochains.d_comp_d : + d A n ≫ d A (n + 1) = 0 := by + simpa [CochainComplex.of] using (inhomogeneousCochains A).d_comp_d n (n + 1) (n + 2) /-- Given a `k`-linear `G`-representation `A`, the complex of inhomogeneous cochains is isomorphic -to `Hom(P, A)`, where `P` is the standard resolution of `k` as a trivial `G`-representation. -/ -def inhomogeneousCochainsIso : inhomogeneousCochains A ≅ linearYonedaObjResolution A := by - refine HomologicalComplex.Hom.isoOfComponents (fun i => - (Rep.diagonalHomEquiv i A).toModuleIso.symm) ?_ +to `Hom(P, A)`, where `P` is the bar resolution of `k` as a trivial `G`-representation. -/ +def inhomogeneousCochainsIso : + inhomogeneousCochains A ≅ (barComplex k G).linearYonedaObj k A := by + refine HomologicalComplex.Hom.isoOfComponents + (fun i => (Rep.freeLiftLEquiv (Fin i → G) A).toModuleIso.symm) ?_ rintro i j (h : i + 1 = j) subst h - ext - simp only [ChainComplex.linearYonedaObj_X, linearYoneda_obj_obj_carrier, CochainComplex.of_x, - linearYoneda_obj_obj_isAddCommGroup, linearYoneda_obj_obj_isModule, Iso.symm_hom, - LinearEquiv.toModuleIso_inv, ChainComplex.linearYonedaObj_d, ModuleCat.hom_comp, - ModuleCat.hom_ofHom, LinearMap.coe_comp, Function.comp_apply, Linear.leftComp_apply, - inhomogeneousCochains.d_def, d_eq, LinearEquiv.toModuleIso_hom, ModuleCat.ofHom_comp, - Category.assoc, LinearEquiv.comp_coe, LinearEquiv.self_trans_symm, LinearEquiv.refl_toLinearMap, - LinearMap.id_comp, LinearEquiv.coe_coe] - rfl + simp [d_eq, -LinearEquiv.toModuleIso_hom, -LinearEquiv.toModuleIso_inv] /-- The `n`-cocycles `Zⁿ(G, A)` of a `k`-linear `G`-representation `A`, i.e. the kernel of the `n`th differential in the complex of inhomogeneous cochains. -/ abbrev cocycles (n : ℕ) : ModuleCat k := (inhomogeneousCochains A).cycles n /-- The natural inclusion of the `n`-cocycles `Zⁿ(G, A)` into the `n`-cochains `Cⁿ(G, A).` -/ -abbrev iCocycles (n : ℕ) : cocycles A n ⟶ ModuleCat.of k ((Fin n → G) → A) := +abbrev iCocycles (n : ℕ) : cocycles A n ⟶ (inhomogeneousCochains A).X n := (inhomogeneousCochains A).iCycles n /-- This is the map from `i`-cochains to `j`-cocycles induced by the differential in the complex of inhomogeneous cochains. -/ -abbrev toCocycles (i j : ℕ) : ModuleCat.of k ((Fin i → G) → A) ⟶ cocycles A j := +abbrev toCocycles (i j : ℕ) : (inhomogeneousCochains A).X i ⟶ cocycles A j := (inhomogeneousCochains A).toCycles i j end groupCohomology @@ -238,18 +178,33 @@ open groupCohomology /-- The group cohomology of a `k`-linear `G`-representation `A`, as the cohomology of its complex of inhomogeneous cochains. -/ -def groupCohomology [Group G] (A : Rep k G) (n : ℕ) : ModuleCat k := +def groupCohomology [Group G] [DecidableEq G] (A : Rep k G) (n : ℕ) : ModuleCat k := (inhomogeneousCochains A).homology n /-- The natural map from `n`-cocycles to `n`th group cohomology for a `k`-linear `G`-representation `A`. -/ -abbrev groupCohomologyπ [Group G] (A : Rep k G) (n : ℕ) : +abbrev groupCohomology.π [Group G] [DecidableEq G] (A : Rep k G) (n : ℕ) : groupCohomology.cocycles A n ⟶ groupCohomology A n := (inhomogeneousCochains A).homologyπ n +@[deprecated (since := "2025-06-11")] +noncomputable alias groupCohomologyπ := groupCohomology.π + +@[elab_as_elim] +theorem groupCohomology_induction_on [Group G] [DecidableEq G] {A : Rep k G} {n : ℕ} + {C : groupCohomology A n → Prop} (x : groupCohomology A n) + (h : ∀ x : cocycles A n, C (π A n x)) : C x := by + rcases (ModuleCat.epi_iff_surjective (π A n)).1 inferInstance x with ⟨y, rfl⟩ + exact h y + /-- The `n`th group cohomology of a `k`-linear `G`-representation `A` is isomorphic to `Extⁿ(k, A)` (taken in `Rep k G`), where `k` is a trivial `k`-linear `G`-representation. -/ def groupCohomologyIsoExt [Group G] [DecidableEq G] (A : Rep k G) (n : ℕ) : groupCohomology A n ≅ ((Ext k (Rep k G) n).obj (Opposite.op <| Rep.trivial k G k)).obj A := isoOfQuasiIsoAt (HomotopyEquiv.ofIso (inhomogeneousCochainsIso A)).hom n ≪≫ - (Rep.standardResolution.extIso k G A n).symm + (Rep.barResolution.extIso k G A n).symm + +lemma isZero_groupCohomology_succ_of_subsingleton + [Group G] [Subsingleton G] (A : Rep k G) (n : ℕ) : + Limits.IsZero (groupCohomology A (n + 1)) := + (isZero_Ext_succ_of_projective (Rep.trivial k G k) A n).of_iso <| groupCohomologyIsoExt _ _ diff --git a/Mathlib/RepresentationTheory/GroupCohomology/Functoriality.lean b/Mathlib/RepresentationTheory/GroupCohomology/Functoriality.lean index 6e294367eb5e89..592394f465174e 100644 --- a/Mathlib/RepresentationTheory/GroupCohomology/Functoriality.lean +++ b/Mathlib/RepresentationTheory/GroupCohomology/Functoriality.lean @@ -34,12 +34,16 @@ open Rep CategoryTheory Representation variable {k G H : Type u} [CommRing k] [Group G] [Group H] {A : Rep k H} {B : Rep k G} (f : G →* H) (φ : (Action.res _ f).obj A ⟶ B) (n : ℕ) +section + theorem congr {f₁ f₂ : G →* H} (h : f₁ = f₂) {φ : (Action.res _ f₁).obj A ⟶ B} {T : Type*} (F : (f : G →* H) → (φ : (Action.res _ f).obj A ⟶ B) → T) : F f₁ φ = F f₂ (h ▸ φ) := by subst h rfl +variable [DecidableEq G] [DecidableEq H] + /-- Given a group homomorphism `f : G →* H` and a representation morphism `φ : Res(f)(A) ⟶ B`, this is the chain map sending `x : Hⁿ → A` to `(g : Gⁿ) ↦ φ (x (f ∘ g))`. -/ @[simps! -isSimp f f_hom] @@ -50,8 +54,8 @@ noncomputable def cochainsMap : comm' i j (hij : _ = _) := by subst hij ext - funext - simpa [inhomogeneousCochains.d_apply, Fin.comp_contractNth] using (hom_comm_apply φ _ _).symm + simpa [inhomogeneousCochains.d_hom_apply, Fin.comp_contractNth] + using (hom_comm_apply φ _ _).symm @[simp] lemma cochainsMap_id : @@ -59,14 +63,17 @@ lemma cochainsMap_id : rfl @[simp] -lemma cochainsMap_id_f_eq_compLeft {A B : Rep k G} (f : A ⟶ B) (i : ℕ) : - (cochainsMap (MonoidHom.id G) f).f i = ModuleCat.ofHom (f.hom.hom.compLeft _) := by +lemma cochainsMap_id_f_hom_eq_compLeft {A B : Rep k G} (f : A ⟶ B) (i : ℕ) : + ((cochainsMap (MonoidHom.id G) f).f i).hom = f.hom.hom.compLeft _ := by ext rfl +@[deprecated (since := "2025-06-11")] +alias cochainsMap_id_f_eq_compLeft := cochainsMap_id_f_hom_eq_compLeft + @[reassoc] -lemma cochainsMap_comp {G H K : Type u} [Group G] [Group H] [Group K] - {A : Rep k K} {B : Rep k H} {C : Rep k G} (f : H →* K) (g : G →* H) +lemma cochainsMap_comp {G H K : Type u} [Group G] [DecidableEq G] [Group H] [DecidableEq H] + [Group K] [DecidableEq K] {A : Rep k K} {B : Rep k H} {C : Rep k G} (f : H →* K) (g : G →* H) (φ : (Action.res _ f).obj A ⟶ B) (ψ : (Action.res _ g).obj B ⟶ C) : cochainsMap (f.comp g) ((Action.res _ g).map φ ≫ ψ) = cochainsMap f φ ≫ cochainsMap g ψ := by @@ -108,6 +115,18 @@ noncomputable abbrev cocyclesMap (n : ℕ) : groupCohomology.cocycles A n ⟶ groupCohomology.cocycles B n := HomologicalComplex.cyclesMap (cochainsMap f φ) n +@[simp] +lemma cocyclesMap_id : cocyclesMap (MonoidHom.id G) (𝟙 B) n = 𝟙 _ := + HomologicalComplex.cyclesMap_id _ _ + +@[reassoc] +lemma cocyclesMap_comp {G H K : Type u} [Group G] [DecidableEq G] [Group H] [DecidableEq H] + [Group K] [DecidableEq K] {A : Rep k K} {B : Rep k H} {C : Rep k G} (f : H →* K) (g : G →* H) + (φ : (Action.res _ f).obj A ⟶ B) (ψ : (Action.res _ g).obj B ⟶ C) (n : ℕ) : + cocyclesMap (f.comp g) ((Action.res _ g).map φ ≫ ψ) n = + cocyclesMap f φ n ≫ cocyclesMap g ψ n := by + simp [cocyclesMap, ← HomologicalComplex.cyclesMap_comp, ← cochainsMap_comp] + @[reassoc] theorem cocyclesMap_id_comp {A B C : Rep k G} (φ : A ⟶ B) (ψ : B ⟶ C) (n : ℕ) : cocyclesMap (MonoidHom.id G) (φ ≫ ψ) n = @@ -121,6 +140,21 @@ noncomputable abbrev map (n : ℕ) : groupCohomology A n ⟶ groupCohomology B n := HomologicalComplex.homologyMap (cochainsMap f φ) n +@[reassoc, elementwise] +theorem π_map (n : ℕ) : + π A n ≫ map f φ n = cocyclesMap f φ n ≫ π B n := by + simp [map, cocyclesMap] + +@[simp] +lemma map_id : map (MonoidHom.id G) (𝟙 B) n = 𝟙 _ := HomologicalComplex.homologyMap_id _ _ + +@[reassoc] +lemma map_comp {G H K : Type u} [Group G] [DecidableEq G] [Group H] [DecidableEq H] + [Group K] [DecidableEq K] {A : Rep k K} {B : Rep k H} {C : Rep k G} (f : H →* K) (g : G →* H) + (φ : (Action.res _ f).obj A ⟶ B) (ψ : (Action.res _ g).obj B ⟶ C) (n : ℕ) : + map (f.comp g) ((Action.res _ g).map φ ≫ ψ) n = map f φ n ≫ map g ψ n := by + simp [map, ← HomologicalComplex.homologyMap_comp, ← cochainsMap_comp] + @[reassoc] theorem map_id_comp {A B C : Rep k G} (φ : A ⟶ B) (ψ : B ⟶ C) (n : ℕ) : map (MonoidHom.id G) (φ ≫ ψ) n = @@ -147,7 +181,7 @@ noncomputable abbrev fThree : ModuleCat.ofHom <| φ.hom.hom.compLeft (G × G × G) ∘ₗ LinearMap.funLeft k A (Prod.map f (Prod.map f f)) -@[reassoc] +@[reassoc (attr := simp), elementwise (attr := simp)] lemma cochainsMap_f_0_comp_zeroCochainsIso : (cochainsMap f φ).f 0 ≫ (zeroCochainsIso B).hom = (zeroCochainsIso A).hom ≫ φ.hom := by ext x @@ -157,7 +191,7 @@ lemma cochainsMap_f_0_comp_zeroCochainsIso : @[deprecated (since := "2025-05-09")] alias cochainsMap_f_0_comp_zeroCochainsLequiv := cochainsMap_f_0_comp_zeroCochainsIso -@[reassoc] +@[reassoc (attr := simp), elementwise (attr := simp)] lemma cochainsMap_f_1_comp_oneCochainsIso : (cochainsMap f φ).f 1 ≫ (oneCochainsIso B).hom = (oneCochainsIso A).hom ≫ fOne f φ := by ext x @@ -167,7 +201,7 @@ lemma cochainsMap_f_1_comp_oneCochainsIso : @[deprecated (since := "2025-05-09")] alias cochainsMap_f_1_comp_oneCochainsLequiv := cochainsMap_f_1_comp_oneCochainsIso -@[reassoc] +@[reassoc (attr := simp), elementwise (attr := simp)] lemma cochainsMap_f_2_comp_twoCochainsIso : (cochainsMap f φ).f 2 ≫ (twoCochainsIso B).hom = (twoCochainsIso A).hom ≫ fTwo f φ := by ext x g @@ -178,7 +212,7 @@ lemma cochainsMap_f_2_comp_twoCochainsIso : @[deprecated (since := "2025-05-09")] alias cochainsMap_f_2_comp_twoCochainsLequiv := cochainsMap_f_2_comp_twoCochainsIso -@[reassoc] +@[reassoc (attr := simp), elementwise (attr := simp)] lemma cochainsMap_f_3_comp_threeCochainsIso : (cochainsMap f φ).f 3 ≫ (threeCochainsIso B).hom = (threeCochainsIso A).hom ≫ fThree f φ := by ext x g @@ -189,8 +223,12 @@ lemma cochainsMap_f_3_comp_threeCochainsIso : @[deprecated (since := "2025-05-09")] alias cochainsMap_f_3_comp_threeCochainsLequiv := cochainsMap_f_3_comp_threeCochainsIso +end + open ShortComplex +section H0 + /-- Given a group homomorphism `f : G →* H` and a representation morphism `φ : Res(f)(A) ⟶ B`, this is induced map `Aᴴ ⟶ Bᴳ`. -/ noncomputable abbrev H0Map : H0 A ⟶ H0 B := @@ -227,16 +265,20 @@ instance mono_H0Map_of_mono {A B : Rep k G} (f : A ⟶ B) [Mono f] : (ModuleCat.mono_iff_injective _).2 fun _ _ hxy => Subtype.ext <| (mono_iff_injective f).1 ‹_› (Subtype.ext_iff.1 hxy) +variable [DecidableEq G] [DecidableEq H] in @[reassoc (attr := simp), elementwise (attr := simp)] theorem cocyclesMap_comp_isoZeroCocycles_hom : cocyclesMap f φ 0 ≫ (isoZeroCocycles B).hom = (isoZeroCocycles A).hom ≫ H0Map f φ := by - have := cochainsMap_f_0_comp_zeroCochainsIso f φ - simp_all [← cancel_mono (shortComplexH0 B).f] + simp [← cancel_mono (shortComplexH0 B).f] +variable [DecidableEq G] [DecidableEq H] in @[reassoc (attr := simp), elementwise (attr := simp)] theorem map_comp_isoH0_hom : map f φ 0 ≫ (isoH0 B).hom = (isoH0 A).hom ≫ H0Map f φ := by - simp [← cancel_epi (groupCohomologyπ _ _)] + simp [← cancel_epi (π _ _)] + +end H0 +section H1 /-- Given a group homomorphism `f : G →* H` and a representation morphism `φ : Res(f)(A) ⟶ B`, this is the induced map from the short complex `A --dZero--> Fun(H, A) --dOne--> Fun(H × H, A)` @@ -298,6 +340,7 @@ lemma coe_mapOneCocycles (x) : @[deprecated (since := "2025-05-09")] alias mapOneCocycles_comp_subtype := mapOneCocycles_comp_i +variable [DecidableEq G] [DecidableEq H] in @[reassoc (attr := simp), elementwise (attr := simp)] lemma cocyclesMap_comp_isoOneCocycles_hom : cocyclesMap f φ 1 ≫ (isoOneCocycles B).hom = (isoOneCocycles A).hom ≫ mapOneCocycles f φ := by @@ -339,10 +382,11 @@ lemma H1π_comp_H1Map : H1π A ≫ H1Map f φ = mapOneCocycles f φ ≫ H1π B := by simp +variable [DecidableEq G] [DecidableEq H] in @[reassoc (attr := simp), elementwise (attr := simp)] lemma map_comp_isoH1_hom : map f φ 1 ≫ (isoH1 B).hom = (isoH1 A).hom ≫ H1Map f φ := by - simp [← cancel_epi (groupCohomologyπ _ _), H1Map, Category.assoc] + simp [← cancel_epi (π _ _), H1Map, Category.assoc] @[simp] theorem H1Map_one (φ : (Action.res _ 1).obj A ⟶ B) : @@ -378,7 +422,7 @@ instance : Mono (H1InfRes A S).f := by rcases hx with ⟨y, hy⟩ refine ⟨⟨y, fun s => ?_⟩, Subtype.ext <| funext fun g => Quotient.inductionOn' g fun g => Subtype.ext <| congr_fun (Subtype.ext_iff.1 hy) g⟩ - simpa [coe_mapOneCocycles (x := x), sub_eq_zero, moduleCatToCycles, shortComplexH1, + simpa [coe_mapOneCocycles (x := x), sub_eq_zero, LinearMap.codRestrict, shortComplexH1, (QuotientGroup.eq_one_iff s.1).2 s.2] using congr_fun (Subtype.ext_iff.1 hy) s.1 /-- Given a `G`-representation `A` and a normal subgroup `S ≤ G`, the short complex @@ -420,11 +464,13 @@ lemma H1InfRes_exact : (H1InfRes A S).Exact := by Submodule.mkQ_apply, H1π_comp_H1Map_apply, Submodule.Quotient.eq] use y refine Subtype.ext <| funext fun g => ?_ - simp only [moduleCatToCycles_apply_coe, AddSubgroupClass.coe_sub] + simp only [LinearMap.codRestrict_apply, AddSubgroupClass.coe_sub] simp [shortComplexH1, coe_mapOneCocycles (QuotientGroup.mk' S), oneCocycles.coe_mk (A := A.quotientToInvariants S), ← sub_sub] end InfRes +end H1 +section H2 /-- Given a group homomorphism `f : G →* H` and a representation morphism `φ : Res(f)(A) ⟶ B`, this is the induced map from the short complex @@ -486,6 +532,7 @@ lemma coe_mapTwoCocycles (x) : @[deprecated (since := "2025-05-09")] alias mapTwoCocycles_comp_subtype := mapTwoCocycles_comp_i +variable [DecidableEq G] [DecidableEq H] in @[reassoc (attr := simp), elementwise (attr := simp)] lemma cocyclesMap_comp_isoTwoCocycles_hom : cocyclesMap f φ 2 ≫ (isoTwoCocycles B).hom = (isoTwoCocycles A).hom ≫ mapTwoCocycles f φ := by @@ -520,10 +567,15 @@ lemma H2π_comp_H2Map : H2π A ≫ H2Map f φ = mapTwoCocycles f φ ≫ H2π B := by simp +variable [DecidableEq G] [DecidableEq H] in @[reassoc (attr := simp), elementwise (attr := simp)] lemma map_comp_isoH2_hom : map f φ 2 ≫ (isoH2 B).hom = (isoH2 A).hom ≫ H2Map f φ := by - simp [← cancel_epi (groupCohomologyπ _ _), H2Map, Category.assoc] + simp [← cancel_epi (π _ _), H2Map, Category.assoc] + +end H2 + +variable [DecidableEq G] variable (k G) in /-- The functor sending a representation to its complex of inhomogeneous cochains. -/ diff --git a/Mathlib/RepresentationTheory/GroupCohomology/LowDegree.lean b/Mathlib/RepresentationTheory/GroupCohomology/LowDegree.lean index ca6eff91f2dabf..53c1578341d17d 100644 --- a/Mathlib/RepresentationTheory/GroupCohomology/LowDegree.lean +++ b/Mathlib/RepresentationTheory/GroupCohomology/LowDegree.lean @@ -63,6 +63,8 @@ namespace groupCohomology section Cochains +variable [DecidableEq G] + /-- The 0th object in the complex of inhomogeneous cochains of `A : Rep k G` is isomorphic to `A` as a `k`-module. -/ def zeroCochainsIso : (inhomogeneousCochains A).X 0 ≅ A.V := @@ -115,6 +117,7 @@ theorem dZero_ker_eq_invariants : LinearMap.ker (dZero A).hom = invariants A.ρ rw [dZero_hom_apply, isTrivial_apply, sub_self] rfl +@[reassoc (attr := simp), elementwise (attr := simp)] lemma subtype_comp_dZero : ModuleCat.ofHom (A.ρ.invariants.subtype) ≫ dZero A = 0 := by ext ⟨x, hx⟩ g replace hx := hx g @@ -146,6 +149,8 @@ def dTwo : ModuleCat.of k (G × G → A) ⟶ ModuleCat.of k (G × G × G → A) add_sub_assoc, add_sub_assoc] map_smul' r x := funext fun g => by dsimp; simp only [map_smul, smul_add, smul_sub] } +variable [DecidableEq G] + /-- Let `C(G, A)` denote the complex of inhomogeneous cochains of `A : Rep k G`. This lemma says `dZero` gives a simpler expression for the 0th differential: that is, the following square commutes: @@ -170,6 +175,12 @@ theorem comp_dZero_eq : @[deprecated (since := "2025-05-09")] alias dZero_comp_eq := comp_dZero_eq +@[reassoc (attr := simp), elementwise (attr := simp)] +theorem eq_dZero_comp_inv : + (zeroCochainsIso A).inv ≫ (inhomogeneousCochains A).d 0 1 = + dZero A ≫ (oneCochainsIso A).inv := + (CommSq.horiz_inv ⟨comp_dZero_eq A⟩).w + /-- Let `C(G, A)` denote the complex of inhomogeneous cochains of `A : Rep k G`. This lemma says `dOne` gives a simpler expression for the 1st differential: that is, the following square commutes: @@ -195,6 +206,12 @@ theorem comp_dOne_eq : @[deprecated (since := "2025-05-09")] alias dOne_comp_eq := comp_dOne_eq +@[reassoc (attr := simp), elementwise (attr := simp)] +theorem eq_dOne_comp_inv : + (oneCochainsIso A).inv ≫ (inhomogeneousCochains A).d 1 2 = + dOne A ≫ (twoCochainsIso A).inv := + (CommSq.horiz_inv ⟨comp_dOne_eq A⟩).w + /-- Let `C(G, A)` denote the complex of inhomogeneous cochains of `A : Rep k G`. This lemma says `dTwo` gives a simpler expression for the 2nd differential: that is, the following square commutes: @@ -221,29 +238,43 @@ theorem comp_dTwo_eq : @[deprecated (since := "2025-05-09")] alias dTwo_comp_eq := comp_dTwo_eq +@[reassoc (attr := simp), elementwise (attr := simp)] +theorem eq_dTwo_comp_inv : + (twoCochainsIso A).inv ≫ (inhomogeneousCochains A).d 2 3 = + dTwo A ≫ (threeCochainsIso A).inv := + (CommSq.horiz_inv ⟨comp_dTwo_eq A⟩).w + +omit [DecidableEq G] in @[reassoc (attr := simp), elementwise (attr := simp)] theorem dZero_comp_dOne : dZero A ≫ dOne A = 0 := by - simp [(Iso.eq_inv_comp _).2 (comp_dOne_eq A), (Iso.eq_inv_comp _).2 (comp_dZero_eq A)] + ext + simp [Pi.zero_apply (M := fun _ => A)] @[deprecated (since := "2025-05-14")] alias dOne_comp_dZero := dZero_comp_dOne +omit [DecidableEq G] in @[reassoc (attr := simp), elementwise (attr := simp)] theorem dOne_comp_dTwo : dOne A ≫ dTwo A = 0 := by - simp [(Iso.eq_inv_comp _).2 (comp_dOne_eq A), (Iso.eq_inv_comp _).2 (comp_dTwo_eq A)] + ext f g + simp [mul_assoc, Pi.zero_apply (M := fun _ => A)] + abel @[deprecated (since := "2025-05-14")] alias dTwo_comp_dOne := dOne_comp_dTwo open ShortComplex /-- The (exact) short complex `A.ρ.invariants ⟶ A ⟶ (G → A)`. -/ +@[simps! -isSimp f g] def shortComplexH0 : ShortComplex (ModuleCat k) := mk _ _ (subtype_comp_dZero A) /-- The short complex `A --dZero--> Fun(G, A) --dOne--> Fun(G × G, A)`. -/ +@[simps! -isSimp f g] def shortComplexH1 : ShortComplex (ModuleCat k) := mk (dZero A) (dOne A) (dZero_comp_dOne A) /-- The short complex `Fun(G, A) --dOne--> Fun(G × G, A) --dTwo--> Fun(G × G × G, A)`. -/ +@[simps! -isSimp f g] def shortComplexH2 : ShortComplex (ModuleCat k) := mk (dOne A) (dTwo A) (dOne_comp_dTwo A) @@ -297,6 +328,10 @@ theorem dZero_apply_mem_oneCocycles (x : A) : dZero A x ∈ oneCocycles A := dZero_comp_dOne_apply _ _ +@[simp] +theorem oneCocycles.dOne_apply (x : oneCocycles A) : + dOne A x = 0 := x.2 + theorem oneCocycles_map_mul_of_isTrivial [A.IsTrivial] (f : oneCocycles A) (g h : G) : f (g * h) = f g + f h := by rw [(mem_oneCocycles_iff f).1 f.2, isTrivial_apply A.ρ g (f h), add_comm] @@ -323,9 +358,7 @@ def oneCocyclesIsoOfIsTrivial [hA : A.IsTrivial] : map_smul' _ _ := rfl invFun f := { val := f - property := mem_oneCocycles_of_addMonoidHom f } - left_inv f := by ext; rfl - right_inv f := by ext; rfl } + property := mem_oneCocycles_of_addMonoidHom f } } @[deprecated (since := "2025-05-09")] noncomputable alias oneCocyclesLequivOfIsTrivial := oneCocyclesIsoOfIsTrivial @@ -378,6 +411,10 @@ theorem dOne_apply_mem_twoCocycles (x : G → A) : dOne A x ∈ twoCocycles A := dOne_comp_dTwo_apply _ _ +@[simp] +theorem twoCocycles.dTwo_apply (x : twoCocycles A) : + dTwo A x = 0 := x.2 + end Cocycles section Coboundaries @@ -800,7 +837,7 @@ group homs `G → A`. -/ def H1IsoOfIsTrivial [A.IsTrivial] : H1 A ≅ ModuleCat.of k (Additive G →+ A) := (Submodule.quotEquivOfEqBot _ (by - simp [shortComplexH1, ShortComplex.moduleCatToCycles, Submodule.eq_bot_iff])).toModuleIso ≪≫ + simp [shortComplexH1, LinearMap.codRestrict, Submodule.eq_bot_iff])).toModuleIso ≪≫ (oneCocyclesIsoOfIsTrivial A) @[deprecated (since := "2025-05-09")] @@ -846,6 +883,8 @@ lemma shortComplexH0_exact : (shortComplexH0 A).Exact := by rw [← sub_eq_zero] exact congr_fun hx g +variable [DecidableEq G] + /-- The arrow `A --dZero--> Fun(G, A)` is isomorphic to the differential `(inhomogeneousCochains A).d 0 1` of the complex of inhomogeneous cochains of `A`. -/ @[simps! hom_left hom_right inv_left inv_right] @@ -882,96 +921,121 @@ def isoH0 : groupCohomology A 0 ≅ H0 A := (CochainComplex.isoHomologyπ₀ _).symm ≪≫ isoZeroCocycles A @[reassoc (attr := simp), elementwise (attr := simp)] -lemma groupCohomologyπ_comp_isoH0_hom : - groupCohomologyπ A 0 ≫ (isoH0 A).hom = (isoZeroCocycles A).hom := by +lemma π_comp_isoH0_hom : + π A 0 ≫ (isoH0 A).hom = (isoZeroCocycles A).hom := by simp [isoH0] +@[deprecated (since := "2025-06-12")] +alias groupCohomologyπ_comp_isoH0_hom := π_comp_isoH0_hom + end H0 section H1 +variable [DecidableEq G] + /-- The short complex `A --dZero--> Fun(G, A) --dOne--> Fun(G × G, A)` is isomorphic to the 1st short complex associated to the complex of inhomogeneous cochains of `A`. -/ @[simps! hom inv] -def shortComplexH1Iso : (inhomogeneousCochains A).sc' 0 1 2 ≅ shortComplexH1 A := - isoMk (zeroCochainsIso A) (oneCochainsIso A) - (twoCochainsIso A) (comp_dZero_eq A) (comp_dOne_eq A) +def shortComplexH1Iso : (inhomogeneousCochains A).sc 1 ≅ shortComplexH1 A := + (inhomogeneousCochains A).isoSc' 0 1 2 (by simp) (by simp) ≪≫ + isoMk (zeroCochainsIso A) (oneCochainsIso A) (twoCochainsIso A) + (comp_dZero_eq A) (comp_dOne_eq A) /-- The 1-cocycles of the complex of inhomogeneous cochains of `A` are isomorphic to `oneCocycles A`, which is a simpler type. -/ def isoOneCocycles : cocycles A 1 ≅ ModuleCat.of k (oneCocycles A) := - (inhomogeneousCochains A).cyclesIsoSc' _ _ _ (by simp) (by simp) ≪≫ - cyclesMapIso (shortComplexH1Iso A) ≪≫ (shortComplexH1 A).moduleCatCyclesIso + cyclesMapIso' (shortComplexH1Iso A) _ (shortComplexH1 A).moduleCatLeftHomologyData @[reassoc (attr := simp), elementwise (attr := simp)] lemma isoOneCocycles_hom_comp_i : (isoOneCocycles A).hom ≫ (shortComplexH1 A).moduleCatLeftHomologyData.i = iCocycles A 1 ≫ (oneCochainsIso A).hom := by - simp [shortComplexH1, isoOneCocycles, oneCocycles] + simp [isoOneCocycles, iCocycles, HomologicalComplex.iCycles, iCycles] @[deprecated (since := "2025-05-09")] alias isoOneCocycles_hom_comp_subtype := isoOneCocycles_hom_comp_i +@[reassoc (attr := simp), elementwise (attr := simp)] +lemma isoOneCocycles_inv_comp_iCocycles : + (isoOneCocycles A).inv ≫ iCocycles A 1 = + (shortComplexH1 A).moduleCatLeftHomologyData.i ≫ (oneCochainsIso A).inv := + (CommSq.horiz_inv ⟨isoOneCocycles_hom_comp_i A⟩).w + @[reassoc (attr := simp), elementwise (attr := simp)] lemma toCocycles_comp_isoOneCocycles_hom : toCocycles A 0 1 ≫ (isoOneCocycles A).hom = (zeroCochainsIso A).hom ≫ (shortComplexH1 A).moduleCatLeftHomologyData.f' := by - simp [isoOneCocycles] + simp [← cancel_mono (shortComplexH1 A).moduleCatLeftHomologyData.i, comp_dZero_eq, + shortComplexH1_f] /-- The 1st group cohomology of `A`, defined as the 1st cohomology of the complex of inhomogeneous cochains, is isomorphic to `oneCocycles A ⧸ oneCoboundaries A`, which is a simpler type. -/ def isoH1 : groupCohomology A 1 ≅ H1 A := - (inhomogeneousCochains A).homologyIsoSc' _ _ _ (by simp) (by simp) ≪≫ - homologyMapIso (shortComplexH1Iso A) ≪≫ (shortComplexH1 A).moduleCatHomologyIso + (leftHomologyIso _).symm ≪≫ (leftHomologyMapIso' (shortComplexH1Iso A) _ _) @[reassoc (attr := simp), elementwise (attr := simp)] -lemma groupCohomologyπ_comp_isoH1_hom : - groupCohomologyπ A 1 ≫ (isoH1 A).hom = (isoOneCocycles A).hom ≫ H1π A := by - simp [isoH1, isoOneCocycles] +lemma π_comp_isoH1_hom : + π A 1 ≫ (isoH1 A).hom = (isoOneCocycles A).hom ≫ H1π A := by + simp [isoH1, isoOneCocycles, π, HomologicalComplex.homologyπ, leftHomologyπ] + +@[deprecated (since := "2025-06-12")] +alias groupCohomologyπ_comp_isoH1_hom := π_comp_isoH1_hom end H1 section H2 +variable [DecidableEq G] + /-- The short complex `Fun(G, A) --dOne--> Fun(G × G, A) --dTwo--> Fun(G × G × G, A)` is isomorphic to the 2nd short complex associated to the complex of inhomogeneous cochains of `A`. -/ @[simps! hom inv] def shortComplexH2Iso : - (inhomogeneousCochains A).sc' 1 2 3 ≅ shortComplexH2 A := - isoMk (oneCochainsIso A) (twoCochainsIso A) (threeCochainsIso A) - (comp_dOne_eq A) (comp_dTwo_eq A) + (inhomogeneousCochains A).sc 2 ≅ shortComplexH2 A := + (inhomogeneousCochains A).isoSc' 1 2 3 (by simp) (by simp) ≪≫ + isoMk (oneCochainsIso A) (twoCochainsIso A) (threeCochainsIso A) + (comp_dOne_eq A) (comp_dTwo_eq A) /-- The 2-cocycles of the complex of inhomogeneous cochains of `A` are isomorphic to `twoCocycles A`, which is a simpler type. -/ def isoTwoCocycles : cocycles A 2 ≅ ModuleCat.of k (twoCocycles A) := - (inhomogeneousCochains A).cyclesIsoSc' _ _ _ (by simp) (by simp) ≪≫ - cyclesMapIso (shortComplexH2Iso A) ≪≫ (shortComplexH2 A).moduleCatCyclesIso + cyclesMapIso' (shortComplexH2Iso A) _ (shortComplexH2 A).moduleCatLeftHomologyData @[reassoc (attr := simp), elementwise (attr := simp)] lemma isoTwoCocycles_hom_comp_i : (isoTwoCocycles A).hom ≫ (shortComplexH2 A).moduleCatLeftHomologyData.i = iCocycles A 2 ≫ (twoCochainsIso A).hom := by - simp [shortComplexH2, isoTwoCocycles, twoCocycles] + simp [isoTwoCocycles, iCocycles, HomologicalComplex.iCycles, iCycles] @[deprecated (since := "2025-05-09")] alias isoTwoCocycles_hom_comp_subtype := isoTwoCocycles_hom_comp_i +@[reassoc (attr := simp), elementwise (attr := simp)] +lemma isoTwoCocycles_inv_comp_iCocycles : + (isoTwoCocycles A).inv ≫ iCocycles A 2 = + (shortComplexH2 A).moduleCatLeftHomologyData.i ≫ (twoCochainsIso A).inv := + (CommSq.horiz_inv ⟨isoTwoCocycles_hom_comp_i A⟩).w + @[reassoc (attr := simp), elementwise (attr := simp)] lemma toCocycles_comp_isoTwoCocycles_hom : toCocycles A 1 2 ≫ (isoTwoCocycles A).hom = (oneCochainsIso A).hom ≫ (shortComplexH2 A).moduleCatLeftHomologyData.f' := by - simp [isoTwoCocycles] + simp [← cancel_mono (shortComplexH2 A).moduleCatLeftHomologyData.i, comp_dOne_eq, + shortComplexH2_f] /-- The 2nd group cohomology of `A`, defined as the 2nd cohomology of the complex of inhomogeneous cochains, is isomorphic to `twoCocycles A ⧸ twoCoboundaries A`, which is a simpler type. -/ def isoH2 : groupCohomology A 2 ≅ H2 A := - (inhomogeneousCochains A).homologyIsoSc' _ _ _ (by simp) (by simp) ≪≫ - homologyMapIso (shortComplexH2Iso A) ≪≫ (shortComplexH2 A).moduleCatHomologyIso + (leftHomologyIso _).symm ≪≫ (leftHomologyMapIso' (shortComplexH2Iso A) _ _) @[reassoc (attr := simp), elementwise (attr := simp)] -lemma groupCohomologyπ_comp_isoH2_hom : - groupCohomologyπ A 2 ≫ (isoH2 A).hom = (isoTwoCocycles A).hom ≫ H2π A := by - simp [isoH2, isoTwoCocycles] +lemma π_comp_isoH2_hom : + π A 2 ≫ (isoH2 A).hom = (isoTwoCocycles A).hom ≫ H2π A := by + simp [isoH2, isoTwoCocycles, π, HomologicalComplex.homologyπ, leftHomologyπ] + +@[deprecated (since := "2025-06-12")] +alias groupCohomologyπ_comp_isoH2_hom := π_comp_isoH2_hom end H2 diff --git a/Mathlib/RepresentationTheory/GroupCohomology/Resolution.lean b/Mathlib/RepresentationTheory/GroupCohomology/Resolution.lean index a4d1a32f0de33f..691dabf0f2eedf 100644 --- a/Mathlib/RepresentationTheory/GroupCohomology/Resolution.lean +++ b/Mathlib/RepresentationTheory/GroupCohomology/Resolution.lean @@ -6,28 +6,23 @@ Authors: Amelia Livingston import Mathlib.Algebra.Category.ModuleCat.Projective import Mathlib.AlgebraicTopology.ExtraDegeneracy import Mathlib.CategoryTheory.Abelian.Ext -import Mathlib.GroupTheory.GroupAction.Ring import Mathlib.RepresentationTheory.Rep import Mathlib.CategoryTheory.Functor.ReflectsIso.Balanced /-! -# The structure of the `k[G]`-module `k[Gⁿ]` +# The standard and bar resolutions of `k` as a trivial `k`-linear `G`-representation -This file contains facts about an important `k[G]`-module structure on `k[Gⁿ]`, where `k` is a -commutative ring and `G` is a group. The module structure arises from the representation -`G →* End(k[Gⁿ])` induced by the diagonal action of `G` on `Gⁿ.` +Given a commutative ring `k` and a group `G`, this file defines two projective resolutions of `k` +as a trivial `k`-linear `G`-representation. -In particular, we define an isomorphism of `k`-linear `G`-representations between `k[Gⁿ⁺¹]` and -`k[G] ⊗ₖ k[Gⁿ]` (on which `G` acts by `ρ(g₁)(g₂ ⊗ x) = (g₁ * g₂) ⊗ x`). +The first one, the standard resolution, has objects `k[Gⁿ⁺¹]` equipped with the diagonal +representation, and differential defined by `(g₀, ..., gₙ) ↦ ∑ (-1)ⁱ • (g₀, ..., ĝᵢ, ..., gₙ)`. -This allows us to define a `k[G]`-basis on `k[Gⁿ⁺¹]`, by mapping the natural `k[G]`-basis of -`k[G] ⊗ₖ k[Gⁿ]` along the isomorphism. - -We then define the standard resolution of `k` as a trivial representation, by -taking the alternating face map complex associated to an appropriate simplicial `k`-linear -`G`-representation. This simplicial object is the `Rep.linearization` of the simplicial `G`-set -given by the universal cover of the classifying space of `G`, `EG`. We prove this simplicial -`G`-set `EG` is isomorphic to the Čech nerve of the natural arrow of `G`-sets `G ⟶ {pt}`. +We define this as the alternating face map complex associated to an appropriate simplicial +`k`-linear `G`-representation. This simplicial object is the `linearization` of the simplicial +`G`-set given by the universal cover of the classifying space of `G`, `EG`. We prove this +simplicial `G`-set `EG` is isomorphic to the Čech nerve of the natural arrow of `G`-sets +`G ⟶ {pt}`. We then use this isomorphism to deduce that as a complex of `k`-modules, the standard resolution of `k` as a trivial `G`-representation is homotopy equivalent to the complex with `k` at 0 and 0 @@ -36,6 +31,23 @@ elsewhere. Putting this material together allows us to define `Rep.standardResolution`, the standard projective resolution of `k` as a trivial `k`-linear `G`-representation. +We then construct the bar resolution. The `n`th object in this complex is the representation on +`Gⁿ →₀ k[G]` defined pointwise by the left regular representation on `k[G]`. The differentials are +defined by sending `(g₀, ..., gₙ)` to +`g₀·(g₁, ..., gₙ) + ∑ (-1)ʲ⁺¹·(g₀, ..., gⱼgⱼ₊₁, ..., gₙ) + (-1)ⁿ⁺¹·(g₀, ..., gₙ₋₁)` for +`j = 0, ... , n - 1`. + +In `RepresentationTheory.Rep` we define an isomorphism `Rep.diagonalSuccIsoFree` between +`k[Gⁿ⁺¹] ≅ (Gⁿ →₀ k[G])` sending `(g₀, ..., gₙ) ↦ g₀·(g₀⁻¹g₁, ..., gₙ₋₁⁻¹gₙ)`. +We show that this isomorphism defines a commutative square with the bar resolution differential and +the standard resolution differential, and thus conclude that the bar resolution differential +squares to zero and that `Rep.diagonalSuccIsoFree` defines an isomorphism between the two +complexes. We carry the exactness properties across this isomorphism to conclude the bar resolution +is a projective resolution too, in `Rep.barResolution`. + +In `RepresentationTheory.GroupCohomology.Basic`, we then use `Rep.barResolution` to define the +inhomogeneous cochains of a representation, useful for computing group cohomology. + ## Main definitions * `groupCohomology.resolution.ofMulActionBasis` @@ -43,23 +55,8 @@ standard projective resolution of `k` as a trivial `k`-linear `G`-representation * `Rep.standardComplex.forget₂ToModuleCatHomotopyEquiv` * `Rep.standardResolution` -## Implementation notes - -We express `k[G]`-module structures on a module `k`-module `V` using the `Representation` -definition. We avoid using instances `Module (G →₀ k) V` so that we do not run into possible -scalar action diamonds. - -We also use the category theory library to bundle the type `k[Gⁿ]` - or more generally `k[H]` when -`H` has `G`-action - and the representation together, as a term of type `Rep k G`, and call it -`Rep.ofMulAction k G H.` This enables us to express the fact that certain maps are -`G`-equivariant by constructing morphisms in the category `Rep k G`, i.e., representations of `G` -over `k`. -/ -/- Porting note: most altered proofs in this file involved changing `simp` to `rw` or `erw`, so -https://github.com/leanprover-community/mathlib4/issues/5026 and -https://github.com/leanprover-community/mathlib4/issues/5164 are relevant. -/ - suppress_compilation noncomputable section @@ -162,94 +159,6 @@ end Basis end groupCohomology.resolution -namespace Rep - -variable (n) [Group G] (A : Rep k G) - -open groupCohomology.resolution - -/-- Given a `k`-linear `G`-representation `A`, the set of representation morphisms -`Hom(k[Gⁿ⁺¹], A)` is `k`-linearly isomorphic to the set of functions `Gⁿ → A`. -/ -noncomputable def diagonalHomEquiv : - (Rep.diagonal k G (n + 1) ⟶ A) ≃ₗ[k] (Fin n → G) → A := - Linear.homCongr k - ((diagonalSuccIsoTensorTrivial k G n).trans - ((Representation.ofMulAction k G G).repOfTprodIso 1)) - (Iso.refl _) ≪≫ₗ - (Rep.MonoidalClosed.linearHomEquivComm _ _ _ ≪≫ₗ Rep.leftRegularHomEquiv _) ≪≫ₗ - (Finsupp.llift A k k (Fin n → G)).symm - -variable {n A} - -/-- Given a `k`-linear `G`-representation `A`, `diagonalHomEquiv` is a `k`-linear isomorphism of -the set of representation morphisms `Hom(k[Gⁿ⁺¹], A)` with `Fun(Gⁿ, A)`. This lemma says that this -sends a morphism of representations `f : k[Gⁿ⁺¹] ⟶ A` to the function -`(g₁, ..., gₙ) ↦ f(1, g₁, g₁g₂, ..., g₁g₂...gₙ).` -/ -theorem diagonalHomEquiv_apply (f : Rep.diagonal k G (n + 1) ⟶ A) (x : Fin n → G) : - diagonalHomEquiv n A f x = f.hom (Finsupp.single (Fin.partialProd x) 1) := by -/- Porting note (https://github.com/leanprover-community/mathlib4/issues/11039): broken proof was - unfold diagonalHomEquiv - simpa only [LinearEquiv.trans_apply, Rep.leftRegularHomEquiv_apply, - MonoidalClosed.linearHomEquivComm_hom, Finsupp.llift_symm_apply, TensorProduct.curry_apply, - Linear.homCongr_apply, Iso.refl_hom, Iso.trans_inv, Action.comp_hom, ModuleCat.comp_def, - LinearMap.comp_apply, Representation.repOfTprodIso_inv_apply, - diagonalSucc_inv_single_single (1 : G) x, one_smul, one_mul] -/ - change f.hom ((diagonalSuccIsoTensorTrivial k G n).inv.hom - (Finsupp.single 1 1 ⊗ₜ[k] Finsupp.single x 1)) = _ - rw [diagonalSuccIsoTensorTrivial_inv_hom_single_single, one_smul, one_mul] - -/-- Given a `k`-linear `G`-representation `A`, `diagonalHomEquiv` is a `k`-linear isomorphism of -the set of representation morphisms `Hom(k[Gⁿ⁺¹], A)` with `Fun(Gⁿ, A)`. This lemma says that the -inverse map sends a function `f : Gⁿ → A` to the representation morphism sending -`(g₀, ... gₙ) ↦ ρ(g₀)(f(g₀⁻¹g₁, g₁⁻¹g₂, ..., gₙ₋₁⁻¹gₙ))`, where `ρ` is the representation attached -to `A`. -/ -theorem diagonalHomEquiv_symm_apply (f : (Fin n → G) → A) (x : Fin (n + 1) → G) : - ((diagonalHomEquiv n A).symm f).hom (Finsupp.single x 1) = - A.ρ (x 0) (f fun i : Fin n => (x (Fin.castSucc i))⁻¹ * x i.succ) := by - unfold diagonalHomEquiv -/- Porting note (https://github.com/leanprover-community/mathlib4/issues/11039): broken proof was - simp only [LinearEquiv.trans_symm, LinearEquiv.symm_symm, LinearEquiv.trans_apply, - Rep.leftRegularHomEquiv_symm_apply, Linear.homCongr_symm_apply, Action.comp_hom, Iso.refl_inv, - Category.comp_id, Rep.MonoidalClosed.linearHomEquivComm_symm_hom, Iso.trans_hom, - ModuleCat.comp_def, LinearMap.comp_apply, Representation.repOfTprodIso_apply, - diagonalSucc_hom_single x (1 : k), TensorProduct.uncurry_apply, Rep.leftRegularHom_hom, - Finsupp.lift_apply, ihom_obj_ρ_def, Rep.ihom_obj_ρ_apply, Finsupp.sum_single_index, zero_smul, - one_smul, Rep.of_ρ, Rep.Action_ρ_eq_ρ, Rep.trivial_def (x 0)⁻¹, Finsupp.llift_apply A k k] -/ - simp only [LinearEquiv.trans_symm, LinearEquiv.symm_symm, LinearEquiv.trans_apply, - leftRegularHomEquiv_symm_apply, Linear.homCongr_symm_apply, Iso.trans_hom, Iso.refl_inv, - Category.comp_id, Action.comp_hom, MonoidalClosed.linearHomEquivComm_symm_hom, - ModuleCat.hom_comp, LinearMap.comp_apply, Action.tensorObj_V, - diagonalSuccIsoTensorTrivial_hom_hom_single x 1] - -- The prototype linter that checks if `erw` could be replaced with `rw` would time out - -- if it replaces the next `erw`s with `rw`s. So we focus down on the relevant part. - conv_lhs => - erw [TensorProduct.uncurry_apply, Finsupp.lift_apply, Finsupp.sum_single_index] - · simp only [one_smul] - erw [Representation.linHom_apply] - simp only [LinearMap.comp_apply, MonoidHom.one_apply, Module.End.one_apply] - erw [Finsupp.llift_apply] - rw [Finsupp.lift_apply] - erw [Finsupp.sum_single_index] - · rw [one_smul] - · rw [zero_smul] - · rw [zero_smul] - -/-- Auxiliary lemma for defining group cohomology, used to show that the isomorphism -`diagonalHomEquiv` commutes with the differentials in two complexes which compute -group cohomology. -/ -theorem diagonalHomEquiv_symm_partialProd_succ (f : (Fin n → G) → A) (g : Fin (n + 1) → G) - (a : Fin (n + 1)) : - ((diagonalHomEquiv n A).symm f).hom (Finsupp.single (Fin.partialProd g ∘ a.succ.succAbove) 1) - = f (Fin.contractNth a (· * ·) g) := by - rw [diagonalHomEquiv_symm_apply] - simp only [Function.comp_apply, Fin.succ_succAbove_zero, Fin.partialProd_zero, map_one, - Fin.succ_succAbove_succ, Module.End.one_apply, Fin.partialProd_succ] - congr - ext - rw [← Fin.partialProd_succ, Fin.inv_partialProd_mul_eq_contractNth] - -end Rep - variable (G) /-- The simplicial `G`-set sending `[n]` to `Gⁿ⁺¹` equipped with the diagonal action of `G`. -/ @@ -336,7 +245,7 @@ variable (k) face map complex of a simplicial `k`-linear `G`-representation. -/ def Rep.standardComplex [Monoid G] := (AlgebraicTopology.alternatingFaceMapComplex (Rep k G)).obj - (classifyingSpaceUniversalCover G ⋙ Rep.linearization k G) + (classifyingSpaceUniversalCover G ⋙ linearization k G) @[deprecated (since := "2025-06-06")] alias groupCohomology.resolution := Rep.standardComplex @@ -490,4 +399,78 @@ def standardResolution.extIso (V : Rep k G) (n : ℕ) : @[deprecated (since := "2025-06-06")] alias groupCohomology.extIso := Rep.standardResolution.extIso +namespace barComplex + +open Rep Finsupp + +variable (n) + +/-- The differential from `Gⁿ⁺¹ →₀ k[G]` to `Gⁿ →₀ k[G]` in the bar resolution of `k` as a trivial +`k`-linear `G`-representation. It sends `(g₀, ..., gₙ)` to +`g₀·(g₁, ..., gₙ) + ∑ (-1)ʲ⁺¹·(g₀, ..., gⱼgⱼ₊₁, ..., gₙ) + (-1)ⁿ⁺¹·(g₀, ..., gₙ₋₁)` for +`j = 0, ... , n - 1`. -/ +def d : free k G Gⁿ⁺¹ ⟶ free k G Gⁿ := + freeLift _ fun g => single (fun i => g i.succ) (single (g 0) 1) + + Finset.univ.sum fun j : Fin (n + 1) => + single (Fin.contractNth j (· * ·) g) (single (1 : G) ((-1 : k) ^ ((j : ℕ) + 1))) + +variable {k G} in +lemma d_single (x : Gⁿ⁺¹) : + (d k G n).hom (single x (single 1 1)) = single (fun i => x i.succ) (Finsupp.single (x 0) 1) + + Finset.univ.sum fun j : Fin (n + 1) => + single (Fin.contractNth j (· * ·) x) (single (1 : G) ((-1 : k) ^ ((j : ℕ) + 1))) := by + simp [d] + +lemma d_comp_diagonalSuccIsoFree_inv_eq : + d k G n ≫ (diagonalSuccIsoFree k G n).inv = + (diagonalSuccIsoFree k G (n + 1)).inv ≫ (standardComplex k G).d (n + 1) n := + free_ext _ _ fun i => by + simpa [diagonalSuccIsoFree_inv_hom_single_single (k := k), d_single (k := k), + standardComplex.d_eq, standardComplex.d_of (k := k) (Fin.partialProd i), Fin.sum_univ_succ, + Fin.partialProd_contractNth] using + congr(single $(by ext j; exact (Fin.partialProd_succ' i j).symm) 1) + +end barComplex + +open barComplex + +/-- The projective resolution of `k` as a trivial `k`-linear `G`-representation with `n`th +differential `(Gⁿ⁺¹ →₀ k[G]) → (Gⁿ →₀ k[G])` sending `(g₀, ..., gₙ)` to +`g₀·(g₁, ..., gₙ) + ∑ (-1)ʲ⁺¹·(g₀, ..., gⱼgⱼ₊₁, ..., gₙ) + (-1)ⁿ⁺¹·(g₀, ..., gₙ₋₁)` for +`j = 0, ... , n - 1`. -/ +noncomputable abbrev barComplex : ChainComplex (Rep k G) ℕ := + ChainComplex.of (fun n => free k G (Fin n → G)) (fun n => d k G n) fun _ => by + ext x + simp [(diagonalSuccIsoFree k G _).comp_inv_eq.1 (d_comp_diagonalSuccIsoFree_inv_eq k G _)] + +namespace barComplex + +@[simp] +theorem d_def : (barComplex k G).d (n + 1) n = d k G n := ChainComplex.of_d _ _ _ _ + +/-- Isomorphism between the bar resolution and standard resolution, with `n`th map +`(Gⁿ →₀ k[G]) → k[Gⁿ⁺¹]` sending `(g₁, ..., gₙ) ↦ (1, g₁, g₁g₂, ..., g₁...gₙ)`. -/ +def isoStandardComplex : barComplex k G ≅ standardComplex k G := + HomologicalComplex.Hom.isoOfComponents (fun i => (diagonalSuccIsoFree k G i).symm) fun i j => by + rintro (rfl : j + 1 = i) + simp only [ChainComplex.of_x, Iso.symm_hom, d_def, d_comp_diagonalSuccIsoFree_inv_eq] + +end barComplex + +/-- The chain complex `barComplex k G` as a projective resolution of `k` as a trivial +`k`-linear `G`-representation. -/ +@[simps complex] +def barResolution : ProjectiveResolution (Rep.trivial k G k) where + complex := barComplex k G + projective n := inferInstanceAs <| Projective (free k G (Fin n → G)) + π := (isoStandardComplex k G).hom ≫ standardComplex.εToSingle₀ k G + +/-- Given a `k`-linear `G`-representation `V`, `Extⁿ(k, V)` (where `k` is the trivial `k`-linear +`G`-representation) is isomorphic to the `n`th cohomology group of `Hom(P, V)`, where `P` is the +bar resolution of `k`. -/ +def barResolution.extIso (V : Rep k G) (n : ℕ) : + ((Ext k (Rep k G) n).obj (Opposite.op <| Rep.trivial k G k)).obj V ≅ + ((barComplex k G).linearYonedaObj k V).homology n := + (barResolution k G).isoExt n V + end Rep diff --git a/Mathlib/RepresentationTheory/Invariants.lean b/Mathlib/RepresentationTheory/Invariants.lean index 34ace81938e3d6..411eeba658651a 100644 --- a/Mathlib/RepresentationTheory/Invariants.lean +++ b/Mathlib/RepresentationTheory/Invariants.lean @@ -168,8 +168,6 @@ def invariantsEquivRepHom (X Y : Rep k G) : (linHom X.ρ Y.ρ).invariants ≃ₗ map_smul' _ _ := rfl invFun f := ⟨f.hom.hom, fun g => (mem_invariants_iff_comm _ g).2 (ModuleCat.hom_ext_iff.mp (f.comm g))⟩ - left_inv _ := by ext; rfl - right_inv _ := by ext; rfl end Rep diff --git a/Mathlib/RepresentationTheory/Rep.lean b/Mathlib/RepresentationTheory/Rep.lean index 5be01e518d90d9..6c1e8f6b4c5a91 100644 --- a/Mathlib/RepresentationTheory/Rep.lean +++ b/Mathlib/RepresentationTheory/Rep.lean @@ -596,6 +596,52 @@ theorem diagonalSuccIsoFree_inv_hom_single (g : G →₀ k) (f : Fin n → G) : rw [single_add, map_add, diagonalSuccIsoFree_inv_hom_single_single] simp_all [sum_add_index'] +variable (n) (A : Rep k G) + +/-- Given a `k`-linear `G`-representation `A`, the set of representation morphisms +`Hom(k[Gⁿ⁺¹], A)` is `k`-linearly isomorphic to the set of functions `Gⁿ → A`. -/ +noncomputable def diagonalHomEquiv : + (Rep.diagonal k G (n + 1) ⟶ A) ≃ₗ[k] (Fin n → G) → A := + Linear.homCongr k (diagonalSuccIsoFree k G n) (Iso.refl _) ≪≫ₗ + freeLiftLEquiv (Fin n → G) A + +variable {n A} + +/-- Given a `k`-linear `G`-representation `A`, `diagonalHomEquiv` is a `k`-linear isomorphism of +the set of representation morphisms `Hom(k[Gⁿ⁺¹], A)` with `Fun(Gⁿ, A)`. This lemma says that this +sends a morphism of representations `f : k[Gⁿ⁺¹] ⟶ A` to the function +`(g₁, ..., gₙ) ↦ f(1, g₁, g₁g₂, ..., g₁g₂...gₙ).` -/ +theorem diagonalHomEquiv_apply (f : Rep.diagonal k G (n + 1) ⟶ A) (x : Fin n → G) : + diagonalHomEquiv n A f x = f.hom (Finsupp.single (Fin.partialProd x) 1) := by + simp [diagonalHomEquiv, Linear.homCongr_apply, + diagonalSuccIsoFree_inv_hom_single_single (k := k)] + +/-- Given a `k`-linear `G`-representation `A`, `diagonalHomEquiv` is a `k`-linear isomorphism of +the set of representation morphisms `Hom(k[Gⁿ⁺¹], A)` with `Fun(Gⁿ, A)`. This lemma says that the +inverse map sends a function `f : Gⁿ → A` to the representation morphism sending +`(g₀, ... gₙ) ↦ ρ(g₀)(f(g₀⁻¹g₁, g₁⁻¹g₂, ..., gₙ₋₁⁻¹gₙ))`, where `ρ` is the representation attached +to `A`. -/ +theorem diagonalHomEquiv_symm_apply (f : (Fin n → G) → A) (x : Fin (n + 1) → G) : + ((diagonalHomEquiv n A).symm f).hom (Finsupp.single x 1) = + A.ρ (x 0) (f fun i : Fin n => (x (Fin.castSucc i))⁻¹ * x i.succ) := by + simp [diagonalHomEquiv, Linear.homCongr_symm_apply, diagonalSuccIsoFree_hom_hom_single (k := k)] + +/-- Auxiliary lemma for defining group cohomology, used to show that the isomorphism +`diagonalHomEquiv` commutes with the differentials in two complexes which compute +group cohomology. -/ +@[deprecated "We no longer use `diagonalHomEquiv` to define group cohomology" +(since := "2025-06-08")] +theorem diagonalHomEquiv_symm_partialProd_succ (f : (Fin n → G) → A) (g : Fin (n + 1) → G) + (a : Fin (n + 1)) : + ((diagonalHomEquiv n A).symm f).hom (Finsupp.single (Fin.partialProd g ∘ a.succ.succAbove) 1) + = f (Fin.contractNth a (· * ·) g) := by + rw [diagonalHomEquiv_symm_apply] + simp only [Function.comp_apply, Fin.succ_succAbove_zero, Fin.partialProd_zero, map_one, + Fin.succ_succAbove_succ, Module.End.one_apply, Fin.partialProd_succ] + congr + ext + rw [← Fin.partialProd_succ, Fin.inv_partialProd_mul_eq_contractNth] + section MonoidalClosed open MonoidalCategory Action @@ -634,7 +680,6 @@ def homEquiv (A B C : Rep k G) : (A ⊗ B ⟶ C) ≃ (B ⟶ (Rep.ihom A).obj C) comm g := ModuleCat.hom_ext <| TensorProduct.ext' fun x y => by simpa using LinearMap.ext_iff.1 (hom_comm_apply f g y) (A.ρ g x) } left_inv _ := Action.Hom.ext (ModuleCat.hom_ext <| TensorProduct.ext' fun _ _ => rfl) - right_inv _ := by ext; rfl variable {A B C} diff --git a/Mathlib/RingTheory/Algebraic/Basic.lean b/Mathlib/RingTheory/Algebraic/Basic.lean index 4e98fb608e0541..088fbe703e0064 100644 --- a/Mathlib/RingTheory/Algebraic/Basic.lean +++ b/Mathlib/RingTheory/Algebraic/Basic.lean @@ -520,8 +520,6 @@ noncomputable def algEquivEquivAlgHom [NoZeroSMulDivisors K L] [Algebra.IsAlgebr (L ≃ₐ[K] L) ≃* (L →ₐ[K] L) where toFun ϕ := ϕ.toAlgHom invFun ϕ := AlgEquiv.ofBijective ϕ (algHom_bijective ϕ) - left_inv _ := by ext; rfl - right_inv _ := by ext; rfl map_mul' _ _ := rfl end Algebra.IsAlgebraic diff --git a/Mathlib/RingTheory/Artinian/Module.lean b/Mathlib/RingTheory/Artinian/Module.lean index 043cf8d2261170..0d1c59ed250883 100644 --- a/Mathlib/RingTheory/Artinian/Module.lean +++ b/Mathlib/RingTheory/Artinian/Module.lean @@ -505,8 +505,6 @@ lemma isPrime_iff_isMaximal (p : Ideal R) : p.IsPrime ↔ p.IsMaximal := def primeSpectrumEquivMaximalSpectrum : PrimeSpectrum R ≃ MaximalSpectrum R where toFun I := ⟨I.asIdeal, isPrime_iff_isMaximal I.asIdeal |>.mp I.isPrime⟩ invFun I := ⟨I.asIdeal, isPrime_iff_isMaximal I.asIdeal |>.mpr I.isMaximal⟩ - left_inv _ := rfl - right_inv _ := rfl lemma primeSpectrumEquivMaximalSpectrum_comp_asIdeal : MaximalSpectrum.asIdeal ∘ primeSpectrumEquivMaximalSpectrum = diff --git a/Mathlib/RingTheory/Bialgebra/Equiv.lean b/Mathlib/RingTheory/Bialgebra/Equiv.lean index 252a202da98179..b8be47d2eb64ae 100644 --- a/Mathlib/RingTheory/Bialgebra/Equiv.lean +++ b/Mathlib/RingTheory/Bialgebra/Equiv.lean @@ -235,8 +235,18 @@ theorem symm_toCoalgEquiv (e : A ≃ₐc[R] B) : theorem invFun_eq_symm : e.invFun = e.symm := rfl +theorem coe_toEquiv_symm : e.toEquiv.symm = e.symm := rfl + +@[simp] +theorem toEquiv_symm : e.symm.toEquiv = e.toEquiv.symm := + rfl + +@[simp] +theorem coe_toEquiv : ⇑e.toEquiv = e := + rfl + @[simp] -theorem coe_toEquiv_symm : e.toEquiv.symm = e.symm := +theorem coe_symm_toEquiv : ⇑e.toEquiv.symm = e.symm := rfl variable {e₁₂ : A ≃ₐc[R] B} {e₂₃ : B ≃ₐc[R] C} diff --git a/Mathlib/RingTheory/Coalgebra/Equiv.lean b/Mathlib/RingTheory/Coalgebra/Equiv.lean index 1fceacb1e3816c..4a9251a301a2ee 100644 --- a/Mathlib/RingTheory/Coalgebra/Equiv.lean +++ b/Mathlib/RingTheory/Coalgebra/Equiv.lean @@ -224,8 +224,18 @@ theorem apply_symm_apply (e : A ≃ₗc[R] B) (x) : theorem invFun_eq_symm : e.invFun = e.symm := rfl +theorem coe_toEquiv_symm : e.toEquiv.symm = e.symm := rfl + +@[simp] +theorem toEquiv_symm : e.symm.toEquiv = e.toEquiv.symm := + rfl + +@[simp] +theorem coe_toEquiv : ⇑e.toEquiv = e := + rfl + @[simp] -theorem coe_toEquiv_symm : e.toEquiv.symm = e.symm := +theorem coe_symm_toEquiv : ⇑e.toEquiv.symm = e.symm := rfl variable {e₁₂ : A ≃ₗc[R] B} {e₂₃ : B ≃ₗc[R] C} diff --git a/Mathlib/RingTheory/Congruence/Opposite.lean b/Mathlib/RingTheory/Congruence/Opposite.lean index 63018aadab33de..71b6c466f5081c 100644 --- a/Mathlib/RingTheory/Congruence/Opposite.lean +++ b/Mathlib/RingTheory/Congruence/Opposite.lean @@ -46,8 +46,6 @@ The congruences of a ring `R` biject to the congruences of the opposite ring `R def opOrderIso : RingCon R ≃o RingCon Rᵐᵒᵖ where toFun := op invFun := unop - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' {c d} := by rw [le_def, le_def]; constructor <;> intro h _ _ h' <;> exact h h' end RingCon diff --git a/Mathlib/RingTheory/DedekindDomain/Different.lean b/Mathlib/RingTheory/DedekindDomain/Different.lean index 20045e85da86bc..337d02bcb3a214 100644 --- a/Mathlib/RingTheory/DedekindDomain/Different.lean +++ b/Mathlib/RingTheory/DedekindDomain/Different.lean @@ -3,11 +3,8 @@ Copyright (c) 2023 Andrew Yang. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Andrew Yang -/ -import Mathlib.RingTheory.DedekindDomain.Ideal -import Mathlib.RingTheory.Discriminant -import Mathlib.RingTheory.DedekindDomain.IntegralClosure import Mathlib.NumberTheory.KummerDedekind -import Mathlib.RingTheory.IntegralClosure.IntegralRestrict +import Mathlib.RingTheory.Finiteness.Quotient import Mathlib.RingTheory.Trace.Quotient /-! @@ -33,13 +30,13 @@ import Mathlib.RingTheory.Trace.Quotient universe u attribute [local instance] FractionRing.liftAlgebra FractionRing.isScalarTower_liftAlgebra + Ideal.Quotient.field variable (A K : Type*) {L : Type u} {B} [CommRing A] [Field K] [CommRing B] [Field L] variable [Algebra A K] [Algebra B L] [Algebra A B] [Algebra K L] [Algebra A L] variable [IsScalarTower A K L] [IsScalarTower A B L] -open nonZeroDivisors IsLocalization Matrix Algebra - +open nonZeroDivisors IsLocalization Matrix Algebra Pointwise Polynomial Submodule section BIsDomain /-- Under the AKLB setting, `Iᵛ := traceDual A K (I : Submodule B L)` is the @@ -81,6 +78,10 @@ lemma le_traceDual_comm {I J : Submodule B L} : lemma le_traceDual_traceDual {I : Submodule B L} : I ≤ Iᵛᵛ := le_traceDual_comm.mpr le_rfl +@[simp] +lemma restrictScalars_traceDual {I : Submodule B L} : + Iᵛ.restrictScalars A = (Algebra.traceForm K L).dualSubmodule (I.restrictScalars A) := rfl + @[simp] lemma traceDual_bot : (⊥ : Submodule B L)ᵛ = ⊤ := by ext; simpa [mem_traceDual, -RingHom.mem_range] using zero_mem _ @@ -131,8 +132,9 @@ lemma map_equiv_traceDual [IsDomain A] [IsFractionRing B L] [IsDomain B] traceDual, traceForm_apply, Submodule.mem_comap, AlgEquiv.toLinearMap_apply, Submodule.mem_mk, AddSubmonoid.mem_mk, AddSubsemigroup.mem_mk, Set.mem_setOf_eq] apply (FractionRing.algEquiv B L).forall_congr - simp only [restrictScalars_mem, traceForm_apply, AlgEquiv.toEquiv_eq_coe, - EquivLike.coe_coe, mem_comap, AlgEquiv.toLinearMap_apply, AlgEquiv.symm_apply_apply] + simp only [restrictScalars_mem, LinearEquiv.coe_coe, AlgEquiv.coe_symm_toLinearEquiv, + traceForm_apply, mem_one, AlgEquiv.toEquiv_eq_coe, EquivLike.coe_coe, mem_comap, + AlgEquiv.symm_apply_apply] refine fun {y} ↦ (forall_congr' fun hy ↦ ?_) rw [Algebra.trace_eq_of_equiv_equiv (FractionRing.algEquiv A K).toRingEquiv (FractionRing.algEquiv B L).toRingEquiv] @@ -388,17 +390,18 @@ lemma dual_injective : end FractionalIdeal +section IsIntegrallyClosed + variable (B) -variable [IsIntegrallyClosed A] [IsDedekindDomain B] +variable [IsIntegrallyClosed A] [IsDedekindDomain B] [NoZeroSMulDivisors A B] /-- The different ideal of an extension of integral domains `B/A` is the inverse of the dual of `A` as an ideal of `B`. See `coeIdeal_differentIdeal` and `coeSubmodule_differentIdeal`. -/ -noncomputable def differentIdeal [NoZeroSMulDivisors A B] : Ideal B := +noncomputable def differentIdeal : Ideal B := (1 / Submodule.traceDual A (FractionRing A) 1 : Submodule B (FractionRing B)).comap (Algebra.linearMap B (FractionRing B)) -lemma coeSubmodule_differentIdeal_fractionRing - [NoZeroSMulDivisors A B] [Algebra.IsIntegral A B] +lemma coeSubmodule_differentIdeal_fractionRing [Algebra.IsIntegral A B] [Algebra.IsSeparable (FractionRing A) (FractionRing B)] [FiniteDimensional (FractionRing A) (FractionRing B)] : coeSubmodule (FractionRing B) (differentIdeal A B) = @@ -417,9 +420,10 @@ lemma coeSubmodule_differentIdeal_fractionRing · exact one_ne_zero section + variable [IsFractionRing B L] -lemma coeSubmodule_differentIdeal [NoZeroSMulDivisors A B] : +lemma coeSubmodule_differentIdeal : coeSubmodule L (differentIdeal A B) = 1 / Submodule.traceDual A K 1 := by have : (FractionRing.algEquiv B L).toLinearEquiv.comp (Algebra.linearMap B (FractionRing B)) = Algebra.linearMap B L := by ext; simp @@ -448,7 +452,7 @@ lemma coeSubmodule_differentIdeal [NoZeroSMulDivisors A B] : variable (L) -lemma coeIdeal_differentIdeal [NoZeroSMulDivisors A B] : +lemma coeIdeal_differentIdeal : ↑(differentIdeal A B) = (FractionalIdeal.dual A K (1 : FractionalIdeal B⁰ L))⁻¹ := by apply FractionalIdeal.coeToSubmodule_injective simp only [FractionalIdeal.coe_div @@ -458,10 +462,19 @@ lemma coeIdeal_differentIdeal [NoZeroSMulDivisors A B] : variable {A K B L} -open Submodule +theorem differentIdeal_ne_bot [Module.Finite A B] + [Algebra.IsSeparable (FractionRing A) (FractionRing B)] : + differentIdeal A B ≠ ⊥ := by + let K := FractionRing A + let L := FractionRing B + have : IsLocalization (Algebra.algebraMapSubmonoid B A⁰) L := + IsIntegralClosure.isLocalization _ K _ _ + have : FiniteDimensional K L := Module.Finite_of_isLocalization A B _ _ A⁰ + rw [ne_eq, ← FractionalIdeal.coeIdeal_inj (K := L), coeIdeal_differentIdeal (K := K)] + simp lemma differentialIdeal_le_fractionalIdeal_iff - {I : FractionalIdeal B⁰ L} (hI : I ≠ 0) [NoZeroSMulDivisors A B] : + {I : FractionalIdeal B⁰ L} (hI : I ≠ 0) : differentIdeal A B ≤ I ↔ (((I⁻¹ :) : Submodule B L).restrictScalars A).map ((Algebra.trace K L).restrictScalars A) ≤ 1 := by rw [coeIdeal_differentIdeal A K L B, FractionalIdeal.inv_le_comm (by simp) hI, @@ -469,7 +482,7 @@ lemma differentialIdeal_le_fractionalIdeal_iff refine le_traceDual_iff_map_le_one.trans ?_ simp -lemma differentialIdeal_le_iff {I : Ideal B} (hI : I ≠ ⊥) [NoZeroSMulDivisors A B] : +lemma differentialIdeal_le_iff {I : Ideal B} (hI : I ≠ ⊥) : differentIdeal A B ≤ I ↔ (((I⁻¹ : FractionalIdeal B⁰ L) : Submodule B L).restrictScalars A).map ((Algebra.trace K L).restrictScalars A) ≤ 1 := (FractionalIdeal.coeIdeal_le_coeIdeal _).symm.trans @@ -523,7 +536,7 @@ end variable (L) {B} open Polynomial Pointwise in -lemma conductor_mul_differentIdeal [NoZeroSMulDivisors A B] +lemma conductor_mul_differentIdeal (x : B) (hx : Algebra.adjoin K {algebraMap B L x} = ⊤) : (conductor A x) * differentIdeal A B = Ideal.span {aeval x (derivative (minpoly A x))} := by classical @@ -564,18 +577,22 @@ lemma conductor_mul_differentIdeal [NoZeroSMulDivisors A B] exact ⟨fun H b ↦ (mul_one b) ▸ H b 1 (one_mem _), fun H _ _ _ ↦ H _⟩ open Polynomial Pointwise in -lemma aeval_derivative_mem_differentIdeal [NoZeroSMulDivisors A B] +lemma aeval_derivative_mem_differentIdeal (x : B) (hx : Algebra.adjoin K {algebraMap B L x} = ⊤) : aeval x (derivative (minpoly A x)) ∈ differentIdeal A B := by refine SetLike.le_def.mp ?_ (Ideal.mem_span_singleton_self _) rw [← conductor_mul_differentIdeal A K L x hx] exact Ideal.mul_le_left +end IsIntegrallyClosed section +variable (L) +variable [IsFractionRing B L] [IsDedekindDomain A] [IsDedekindDomain B] + [NoZeroSMulDivisors A B] [Module.Finite A B] + include K L in -lemma pow_sub_one_dvd_differentIdeal_aux [IsFractionRing B L] [IsDedekindDomain A] - [NoZeroSMulDivisors A B] [Module.Finite A B] +lemma pow_sub_one_dvd_differentIdeal_aux {p : Ideal A} [p.IsMaximal] (P : Ideal B) {e : ℕ} (he : e ≠ 0) (hp : p ≠ ⊥) (hP : P ^ e ∣ p.map (algebraMap A B)) : P ^ (e - 1) ∣ differentIdeal A B := by obtain ⟨a, ha⟩ := (pow_dvd_pow _ (Nat.sub_le e 1)).trans hP @@ -615,7 +632,7 @@ lemma pow_sub_one_dvd_differentIdeal_aux [IsFractionRing B L] [IsDedekindDomain have : trace K L (algebraMap B L z) ∈ (p : FractionalIdeal A⁰ K) := by rw [← algebraMap_intTrace (A := A)] exact ⟨intTrace A B z, this z hz, rfl⟩ - rwa [mul_comm, ← smul_eq_mul, ← LinearMap.map_smul, smul_def, mul_comm, + rwa [mul_comm, ← smul_eq_mul, ← LinearMap.map_smul, Algebra.smul_def, mul_comm, ← IsScalarTower.algebraMap_apply, IsScalarTower.algebraMap_apply A B L, ← hz'] intros x hx rw [← Ideal.Quotient.eq_zero_iff_mem, ← trace_quotient_eq_of_isDedekindDomain, @@ -624,8 +641,7 @@ lemma pow_sub_one_dvd_differentIdeal_aux [IsFractionRing B L] [IsDedekindDomain rw [← map_pow, Ideal.Quotient.eq_zero_iff_mem] exact (Ideal.dvd_iff_le.mp this) <| Ideal.pow_mem_pow hx _ -lemma pow_sub_one_dvd_differentIdeal [IsDedekindDomain A] [NoZeroSMulDivisors A B] - [Module.Finite A B] [Algebra.IsSeparable (FractionRing A) (FractionRing B)] +lemma pow_sub_one_dvd_differentIdeal [Algebra.IsSeparable (FractionRing A) (FractionRing B)] {p : Ideal A} [p.IsMaximal] (P : Ideal B) (e : ℕ) (hp : p ≠ ⊥) (hP : P ^ e ∣ p.map (algebraMap A B)) : P ^ (e - 1) ∣ differentIdeal A B := by have : IsLocalization (algebraMapSubmonoid B A⁰) (FractionRing B) := @@ -636,4 +652,101 @@ lemma pow_sub_one_dvd_differentIdeal [IsDedekindDomain A] [NoZeroSMulDivisors A · rw [he, pow_zero]; exact one_dvd _ exact pow_sub_one_dvd_differentIdeal_aux A (FractionRing A) (FractionRing B) _ he hp hP +theorem not_dvd_differentIdeal_of_intTrace_not_mem + [Algebra.IsSeparable (FractionRing A) (FractionRing B)] + {p : Ideal A} (P Q : Ideal B) (hP : P * Q = Ideal.map (algebraMap A B) p) + (x : B) (hxQ : x ∈ Q) (hx : Algebra.intTrace A B x ∉ p) : + ¬ P ∣ differentIdeal A B := by + by_cases hp : p = ⊥ + · subst hp + simp only [Ideal.map_bot, Ideal.mul_eq_bot] at hP + obtain (rfl|rfl) := hP + · rw [← Ideal.zero_eq_bot, zero_dvd_iff] + exact differentIdeal_ne_bot + · obtain rfl := hxQ + simp at hx + letI : Algebra (A ⧸ p) (B ⧸ Q) := Ideal.Quotient.algebraQuotientOfLEComap (by + rw [← Ideal.map_le_iff_le_comap, ← hP] + exact Ideal.mul_le_left) + let K := FractionRing A + let L := FractionRing B + have : IsLocalization (Algebra.algebraMapSubmonoid B A⁰) L := + IsIntegralClosure.isLocalization _ K _ _ + have : FiniteDimensional K L := Module.Finite_of_isLocalization A B _ _ A⁰ + rw [Ideal.dvd_iff_le] + intro H + replace H := (mul_le_mul_right' H Q).trans_eq hP + replace H := (FractionalIdeal.coeIdeal_le_coeIdeal' _ (P := L) le_rfl).mpr H + rw [FractionalIdeal.coeIdeal_mul, coeIdeal_differentIdeal A K] at H + replace H := FractionalIdeal.mul_le_mul_left H (FractionalIdeal.dual A K 1) + simp only [ne_eq, FractionalIdeal.dual_eq_zero_iff, one_ne_zero, not_false_eq_true, + mul_inv_cancel_left₀] at H + apply hx + suffices Algebra.trace K L (algebraMap B L x) ∈ (p : FractionalIdeal A⁰ K) by + obtain ⟨y, hy, e⟩ := this + rw [← Algebra.algebraMap_intTrace (A := A), Algebra.linearMap_apply, + (IsLocalization.injective _ le_rfl).eq_iff] at e + exact e ▸ hy + refine FractionalIdeal.mul_induction_on (H ⟨_, hxQ, rfl⟩) ?_ ?_ + · rintro x hx _ ⟨y, hy, rfl⟩ + induction hy using Submodule.span_induction generalizing x with + | mem y h => + obtain ⟨y, hy, rfl⟩ := h + obtain ⟨z, hz⟩ := + (FractionalIdeal.mem_dual (by simp)).mp hx 1 ⟨1, trivial, (algebraMap B L).map_one⟩ + simp only [Algebra.traceForm_apply, mul_one] at hz + refine ⟨z * y, Ideal.mul_mem_left _ _ hy, ?_⟩ + rw [Algebra.linearMap_apply, Algebra.linearMap_apply, mul_comm x, + ← IsScalarTower.algebraMap_apply, + ← Algebra.smul_def, LinearMap.map_smul_of_tower, ← hz, + Algebra.smul_def, map_mul, mul_comm] + | zero => simp + | add y z _ _ hy hz => + simp only [map_add, mul_add] + exact Submodule.add_mem _ (hy x hx) (hz x hx) + | smul y z hz IH => + simpa [Algebra.smul_def, mul_assoc, -FractionalIdeal.mem_coeIdeal, mul_left_comm x] using + IH _ (Submodule.smul_mem _ y hx) + · simp only [map_add, forall_exists_index, and_imp] + exact fun _ _ h₁ h₂ ↦ Submodule.add_mem _ h₁ h₂ + +open nonZeroDivisors + +theorem not_dvd_differentIdeal_of_isCoprime_of_isSeparable + [Algebra.IsSeparable (FractionRing A) (FractionRing B)] + {p : Ideal A} [p.IsMaximal] (P Q : Ideal B) [P.IsMaximal] [P.LiesOver p] + (hPQ : IsCoprime P Q) (hP : P * Q = Ideal.map (algebraMap A B) p) + [Algebra.IsSeparable (A ⧸ p) (B ⧸ P)] : + ¬ P ∣ differentIdeal A B := by + letI : Algebra (A ⧸ p) (B ⧸ Q) := Ideal.Quotient.algebraQuotientOfLEComap (by + rw [← Ideal.map_le_iff_le_comap, ← hP] + exact Ideal.mul_le_left) + have : IsScalarTower A (A ⧸ p) (B ⧸ Q) := .of_algebraMap_eq' rfl + have : Module.Finite (A ⧸ p) (B ⧸ Q) := + Module.Finite.of_restrictScalars_finite A (A ⧸ p) (B ⧸ Q) + letI e : (B ⧸ p.map (algebraMap A B)) ≃ₐ[A ⧸ p] ((B ⧸ P) × B ⧸ Q) := + { __ := (Ideal.quotEquivOfEq hP.symm).trans (Ideal.quotientMulEquivQuotientProd P Q hPQ), + commutes' := Quotient.ind fun _ ↦ rfl } + obtain ⟨x, hx⟩ : ∃ x, Algebra.trace (A ⧸ p) (B ⧸ P) x ≠ 0 := by + simpa [LinearMap.ext_iff] using Algebra.trace_ne_zero (A ⧸ p) (B ⧸ P) + obtain ⟨y, hy⟩ := Ideal.Quotient.mk_surjective (e.symm (x, 0)) + refine not_dvd_differentIdeal_of_intTrace_not_mem A P Q hP y ?_ ?_ + · simpa [e, Ideal.Quotient.eq_zero_iff_mem] using congr((e $hy).2) + · rw [← Ideal.Quotient.eq_zero_iff_mem, ← Algebra.trace_quotient_eq_of_isDedekindDomain, + hy, Algebra.trace_eq_of_algEquiv, Algebra.trace_prod_apply] + simpa + +theorem not_dvd_differentIdeal_of_isCoprime + [Algebra.IsSeparable (FractionRing A) (FractionRing B)] + {p : Ideal A} [p.IsMaximal] [Finite (A ⧸ p)] (P Q : Ideal B) [P.IsMaximal] + (hPQ : IsCoprime P Q) (hP : P * Q = Ideal.map (algebraMap A B) p) : + ¬ P ∣ differentIdeal A B := by + have : P.LiesOver p := by + constructor + refine ‹p.IsMaximal›.eq_of_le ?_ ?_ + · simpa using ‹P.IsMaximal›.ne_top + · rw [← Ideal.map_le_iff_le_comap, ← hP] + exact Ideal.mul_le_right + exact not_dvd_differentIdeal_of_isCoprime_of_isSeparable A P Q hPQ hP + end diff --git a/Mathlib/RingTheory/DedekindDomain/Factorization.lean b/Mathlib/RingTheory/DedekindDomain/Factorization.lean index 982d54756b3466..30bf5cae021196 100644 --- a/Mathlib/RingTheory/DedekindDomain/Factorization.lean +++ b/Mathlib/RingTheory/DedekindDomain/Factorization.lean @@ -36,6 +36,8 @@ prove some of its properties. If `I = 0`, we define `val_v(I) = 0`. the fractional ideal `(k)` is equal to the product `∏_v v^(val_v(r) - val_v(s))`. - `FractionalIdeal.finite_factors` : If `I ≠ 0`, then `val_v(I) = 0` for all but finitely many maximal ideals of `R`. +- `IsDedekindDomain.exists_sup_span_eq`: For every ideals `0 < I ≤ J`, + there exists `a` such that `J = I + ⟨a⟩`. ## Implementation notes Since we are only interested in the factorization of nonzero fractional ideals, we define @@ -577,3 +579,195 @@ theorem finite_factors (I : FractionalIdeal R⁰ K) : rw [count_ne_zero K _ hI] end FractionalIdeal + +section div + +/-- In a Dedekind domain, for every ideals `0 < I ≤ J` there exists `a` such that `J = I + ⟨a⟩`. +TODO: Show that this property uniquely characterizes dedekind domains. -/ +lemma IsDedekindDomain.exists_sup_span_eq {I J : Ideal R} (hIJ : I ≤ J) (hI : I ≠ 0) : + ∃ a, I ⊔ Ideal.span {a} = J := by + classical + obtain ⟨I, rfl⟩ := Ideal.dvd_iff_le.mpr hIJ + simp only [ne_eq, mul_eq_zero, not_or] at hI + obtain ⟨hJ, hI⟩ := hI + suffices ∃ a, ∃ K, J * K = Ideal.span {a} ∧ I + K = ⊤ by + obtain ⟨a, K, e, e'⟩ := this + exact ⟨a, by rw [← e, ← Ideal.add_eq_sup, ← mul_add, e', Ideal.mul_top]⟩ + let s := (I.finite_factors hI).toFinset + have : ∀ p ∈ s, J * ∏ q ∈ s, q.asIdeal < J * ∏ q ∈ s \ {p}, q.asIdeal := by + intro p hps + conv_rhs => rw [← mul_one (J * _)] + rw [Finset.prod_eq_mul_prod_diff_singleton hps, ← mul_assoc, + mul_right_comm _ p.asIdeal] + refine mul_lt_mul_of_pos_left ?_ ?_ + · rw [Ideal.one_eq_top, lt_top_iff_ne_top] + exact p.2.ne_top + · rw [Ideal.zero_eq_bot, bot_lt_iff_ne_bot, ← Ideal.zero_eq_bot, + mul_ne_zero_iff, Finset.prod_ne_zero_iff] + exact ⟨hJ, fun x _ ↦ x.3⟩ + choose! a ha ha' using fun p hps ↦ SetLike.exists_of_lt (this p hps) + obtain ⟨K, hK⟩ : J ∣ Ideal.span {∑ p ∈ s, a p} := by + rw [Ideal.dvd_iff_le, Ideal.span_singleton_le_iff_mem] + exact sum_mem fun p hp ↦ Ideal.mul_le_right (ha p hp) + refine ⟨_, _, hK.symm, ?_⟩ + by_contra H + obtain ⟨p, hp, h⟩ := Ideal.exists_le_maximal _ H + let p' : HeightOneSpectrum R := ⟨p, hp.isPrime, fun e ↦ hI (by simp_all)⟩ + have hp's : p' ∈ s := by simpa [p', s, Ideal.dvd_iff_le] using le_sup_left.trans h + have H₁ : J * K ≤ J * p := Ideal.mul_mono_right (le_sup_right.trans h) + replace H₁ := hK.trans_le H₁ (Ideal.mem_span_singleton_self _) + have H₂ : ∑ q ∈ s \ {p'}, a q ∈ J * p := by + refine sum_mem fun q hq ↦ ?_ + rw [Finset.mem_sdiff, Finset.mem_singleton] at hq + refine Ideal.mul_mono_right ?_ (ha q hq.1) + exact Ideal.prod_le_inf.trans (Finset.inf_le (b := p') (by simpa [hp's] using Ne.symm hq.2)) + apply ha' _ hp's + have := IsDedekindDomain.inf_prime_pow_eq_prod s (fun i ↦ i.asIdeal) (fun _ ↦ 1) + (fun i _ ↦ i.prime) (fun i _ j _ e ↦ mt HeightOneSpectrum.ext e) + simp only [pow_one] at this + have inst : Nonempty {x // x ∈ s} := ⟨_, hp's⟩ + rw [← this, Finset.inf_eq_iInf, iInf_subtype', Ideal.mul_iInf, Ideal.mem_iInf] + rintro ⟨q, hq⟩ + by_cases hqp : q = p' + · subst hqp + convert sub_mem H₁ H₂ + rw [Finset.sum_eq_add_sum_diff_singleton hp's, add_sub_cancel_right] + · refine Ideal.mul_mono_right ?_ (ha p' hp's) + exact Ideal.prod_le_inf.trans (Finset.inf_le (b := q) (by simpa [hq] using hqp)) + +/-- In a Dedekind domain, any ideal is spanned by two elements, where one of the element +could be any fixed non-zero element in the ideal. -/ +lemma IsDedekindDomain.exists_eq_span_pair {I : Ideal R} {x : R} (hxI : x ∈ I) (hx : x ≠ 0) : + ∃ y, I = .span {x, y} := by + obtain ⟨y, rfl⟩ := exists_sup_span_eq (I.span_singleton_le_iff_mem.mpr hxI) (by simpa) + simp_rw [← Ideal.span_union, Set.union_singleton, Set.pair_comm x] + use y + +lemma IsDedekindDomain.exists_add_spanSingleton_mul_eq + {a b c : FractionalIdeal R⁰ K} (hac : a ≤ c) (ha : a ≠ 0) (hb : b ≠ 0) : + ∃ x : K, a + FractionalIdeal.spanSingleton R⁰ x * b = c := by + wlog hb' : b = 1 + · obtain ⟨x, e⟩ := this (a := b⁻¹ * a) (b := 1) (c := b⁻¹ * c) + (FractionalIdeal.mul_le_mul_left hac _) (by simp [ha, hb]) one_ne_zero rfl + use x + simpa [hb, ← mul_assoc, mul_add, mul_comm b (.spanSingleton _ _)] using congr(b * $e) + subst hb' + have H : Ideal.span {c.den.1} * a.num ≤ c.num * Ideal.span {a.den.1} := by + rw [← FractionalIdeal.coeIdeal_le_coeIdeal K] + simp only [FractionalIdeal.coeIdeal_mul, FractionalIdeal.coeIdeal_span_singleton, ← + FractionalIdeal.den_mul_self_eq_num'] + ring_nf + exact FractionalIdeal.mul_le_mul_left hac _ + obtain ⟨x, hx⟩ := exists_sup_span_eq H + (by simpa using FractionalIdeal.num_eq_zero_iff.not.mpr ha) + refine ⟨algebraMap R K x / algebraMap R K (a.den.1 * c.den.1), ?_⟩ + refine mul_left_injective₀ (b := .spanSingleton _ + (algebraMap R K (a.den.1 * c.den.1))) ?_ ?_ + · simp [FractionalIdeal.spanSingleton_eq_zero_iff] + · simp only [map_mul, mul_one, add_mul, FractionalIdeal.spanSingleton_mul_spanSingleton, + isUnit_iff_ne_zero, ne_eq, mul_eq_zero, FaithfulSMul.algebraMap_eq_zero_iff, + nonZeroDivisors.coe_ne_zero, or_self, not_false_eq_true, IsUnit.div_mul_cancel] + rw [← FractionalIdeal.spanSingleton_mul_spanSingleton, ← mul_assoc, mul_comm a, + FractionalIdeal.den_mul_self_eq_num', ← mul_assoc, mul_right_comm, + mul_comm c, FractionalIdeal.den_mul_self_eq_num', mul_comm] + simp_rw [← FractionalIdeal.coeIdeal_span_singleton, ← FractionalIdeal.coeIdeal_mul, + ← hx, ← FractionalIdeal.coeIdeal_sup] + +namespace FractionalIdeal + +/-- `c.divMod b a` (i.e. `c / b mod a`) is an arbitrary `x` such that `c = bx + a`. +This is zero if the above is not possible, i.e. when `a = 0` or `b = 0` or `¬ a ≤ c`. -/ +noncomputable +def divMod (c b a : FractionalIdeal R⁰ K) : K := + letI := Classical.propDecidable + if h : a ≤ c ∧ a ≠ 0 ∧ b ≠ 0 then + (IsDedekindDomain.exists_add_spanSingleton_mul_eq h.1 h.2.1 h.2.2).choose else 0 + + +lemma divMod_spec + {a b c : FractionalIdeal R⁰ K} (hac : a ≤ c) (ha : a ≠ 0) (hb : b ≠ 0) : + a + spanSingleton R⁰ (c.divMod b a) * b = c := by + rw [divMod, dif_pos ⟨hac, ha, hb⟩] + exact (IsDedekindDomain.exists_add_spanSingleton_mul_eq hac ha hb).choose_spec + +@[simp] +lemma divMod_zero_left {I J : FractionalIdeal R⁰ K} : I.divMod 0 J = 0 := by + simp [divMod] + +@[simp] +lemma divMod_zero_right {I J : FractionalIdeal R⁰ K} : I.divMod J 0 = 0 := by + simp [divMod] + +@[simp] +lemma zero_divMod {I J : FractionalIdeal R⁰ K} : + (0 : FractionalIdeal R⁰ K).divMod I J = 0 := by + simp [divMod, ← and_assoc] + +lemma divMod_zero_of_not_le {a b c : FractionalIdeal R⁰ K} (hac : ¬ a ≤ c) : + c.divMod b a = 0 := by + simp [divMod, hac] + +/-- Let `I J I' J'` be nonzero fractional ideals in a dedekind domain with `J ≤ I` and `J' ≤ I'`. +If `I/J = I'/J'` in the group of fractional ideals (i.e. ` I * J' = I' * J`), +then `I/J ≃ I'/J'` as quotient `R`-modules. -/ +noncomputable +def quotientEquiv (I J I' J' : FractionalIdeal R⁰ K) + (H : I * J' = I' * J) (h : J ≤ I) (h' : J' ≤ I') (hJ' : J' ≠ 0) (hI : I ≠ 0) : + (I ⧸ J.coeToSubmodule.comap I.coeToSubmodule.subtype) ≃ₗ[R] + I' ⧸ J'.coeToSubmodule.comap I'.coeToSubmodule.subtype := by + haveI : J' ⊓ spanSingleton R⁰ (I'.divMod I J') * I = spanSingleton R⁰ (I'.divMod I J') * J := by + have := FractionalIdeal.sup_mul_inf J' (spanSingleton R⁰ (I'.divMod I J') * I) + rwa [FractionalIdeal.sup_eq_add, divMod_spec h' hJ' hI, mul_left_comm, mul_comm J' I, H, + mul_comm I' J, ← mul_assoc, (mul_left_injective₀ _).eq_iff] at this + rintro rfl + exact hJ' (by simpa using h') + refine .ofBijective (Submodule.mapQ _ _ (LinearMap.restrict + (Algebra.lsmul R _ _ (I'.divMod I J')) ?_) ?_) ⟨?_, ?_⟩ + · intro x hx + refine (divMod_spec h' hJ' hI).le ?_ + exact Submodule.mem_sup_right (mul_mem_mul (mem_spanSingleton_self _ _) hx) + · rw [← Submodule.comap_comp, LinearMap.subtype_comp_restrict, LinearMap.domRestrict, + Submodule.comap_comp] + refine Submodule.comap_mono ?_ + intro x hx + refine (Submodule.mem_inf.mp (this.ge ?_)).1 + simp only [val_eq_coe, Submodule.mem_comap, Algebra.lsmul_coe, smul_eq_mul, mem_coe] + exact mul_mem_mul (mem_spanSingleton_self _ _) hx + · rw [← LinearMap.ker_eq_bot, Submodule.mapQ, Submodule.ker_liftQ, + LinearMap.ker_comp, Submodule.ker_mkQ, ← Submodule.comap_comp, + LinearMap.subtype_comp_restrict, ← le_bot_iff, Submodule.map_le_iff_le_comap, + Submodule.comap_bot, Submodule.ker_mkQ, LinearMap.domRestrict, + Submodule.comap_comp, ← Submodule.map_le_iff_le_comap, + Submodule.map_comap_eq, Submodule.range_subtype] + by_cases H' : I'.divMod I J' = 0 + · obtain rfl : J' = I' := by simpa [H'] using divMod_spec h' hJ' hI + obtain rfl : I = J := mul_left_injective₀ hJ' (H.trans (mul_comm _ _)) + exact inf_le_left + rw [← inv_mul_eq_iff_eq_mul₀ (by simpa [spanSingleton_eq_zero_iff] using H'), mul_inf₀ + (zero_le _), inv_mul_cancel_left₀ (by simpa [spanSingleton_eq_zero_iff] using H')] at this + rw [← this, inf_comm, coe_inf] + refine inf_le_inf ?_ le_rfl + intro x hx + rw [spanSingleton_inv] + convert mul_mem_mul (mem_spanSingleton_self _ _) hx + simp [H'] + · have H : Submodule.map (Algebra.lsmul R R K (I'.divMod I J')) ↑I = + (spanSingleton R⁰ (I'.divMod I J') * I) := by + ext x + simp [Submodule.mem_span_singleton_mul] + rw [← LinearMap.range_eq_top, Submodule.mapQ, Submodule.range_liftQ, + LinearMap.range_comp, LinearMap.restrict, LinearMap.range_codRestrict, + LinearMap.range_domRestrict, ← top_le_iff, H, + ← LinearMap.range_eq_top.mpr (Submodule.mkQ_surjective _), + ← Submodule.map_top, Submodule.map_le_iff_le_comap, Submodule.comap_map_eq, Submodule.ker_mkQ, + ← Submodule.map_le_map_iff_of_injective I'.coeToSubmodule.injective_subtype, + Submodule.map_top, Submodule.map_sup, + Submodule.map_comap_eq, Submodule.map_comap_eq, Submodule.range_subtype, sup_comm, + inf_eq_right.mpr, inf_eq_right.mpr] + · exact le_trans (divMod_spec h' hJ' hI).ge (by simp) + · exact le_trans (by simp) (divMod_spec h' hJ' hI).le + · exact h' + +end FractionalIdeal + +end div diff --git a/Mathlib/RingTheory/DedekindDomain/FiniteAdeleRing.lean b/Mathlib/RingTheory/DedekindDomain/FiniteAdeleRing.lean index 8580e4ac2eeba3..3d3f734e43222e 100644 --- a/Mathlib/RingTheory/DedekindDomain/FiniteAdeleRing.lean +++ b/Mathlib/RingTheory/DedekindDomain/FiniteAdeleRing.lean @@ -5,7 +5,7 @@ Authors: María Inés de Frutos-Fernández -/ import Mathlib.RingTheory.DedekindDomain.AdicValuation import Mathlib.RingTheory.DedekindDomain.Factorization -import Mathlib.Topology.Algebra.RestrictedProduct +import Mathlib.Topology.Algebra.RestrictedProduct.TopologicalSpace /-! # The finite adèle ring of a Dedekind domain diff --git a/Mathlib/RingTheory/DedekindDomain/Ideal.lean b/Mathlib/RingTheory/DedekindDomain/Ideal.lean index 056a902f20a013..3859cc5468dd29 100644 --- a/Mathlib/RingTheory/DedekindDomain/Ideal.lean +++ b/Mathlib/RingTheory/DedekindDomain/Ideal.lean @@ -5,6 +5,7 @@ Authors: Kenji Nakagawa, Anne Baanen, Filippo A. E. Nuccio -/ import Mathlib.Algebra.Algebra.Subalgebra.Pointwise import Mathlib.Algebra.Polynomial.FieldDivision +import Mathlib.Algebra.Order.GroupWithZero.Unbundled.Basic import Mathlib.RingTheory.Spectrum.Maximal.Localization import Mathlib.RingTheory.ChainOfDivisors import Mathlib.RingTheory.DedekindDomain.Basic @@ -532,6 +533,21 @@ theorem mul_left_strictMono [IsDedekindDomain A] {I : FractionalIdeal A⁰ K} (h StrictMono (I * ·) := strictMono_of_le_iff_le fun _ _ => (mul_left_le_iff hI).symm +instance [IsDedekindDomain A] : PosMulReflectLE (FractionalIdeal A⁰ K) where + elim I _ _ := (FractionalIdeal.mul_left_le_iff I.2.ne').mp + +instance [IsDedekindDomain A] : MulPosReflectLE (FractionalIdeal A⁰ K) where + elim I J K e := by + dsimp at *; rwa [mul_comm, mul_comm K, FractionalIdeal.mul_left_le_iff I.2.ne'] at e + +instance [IsDedekindDomain A] : PosMulReflectLE (Ideal A) where + elim I J K e := by + dsimp + rwa [← FractionalIdeal.coeIdeal_le_coeIdeal (FractionRing A), + ← FractionalIdeal.mul_left_le_iff (J := I) (by simpa using I.2.ne'), + ← FractionalIdeal.coeIdeal_mul, ← FractionalIdeal.coeIdeal_mul, + FractionalIdeal.coeIdeal_le_coeIdeal] + /-- This is also available as `_root_.div_eq_mul_inv`, using the `Semifield` instance defined below. -/ @@ -586,11 +602,27 @@ instance FractionalIdeal.cancelCommMonoidWithZero : __ : CommSemiring (FractionalIdeal A⁰ K) := inferInstance mul_left_cancel_of_ne_zero := mul_left_cancel₀ +instance : PosMulStrictMono (FractionalIdeal A⁰ K) := PosMulMono.toPosMulStrictMono + +instance : MulPosStrictMono (FractionalIdeal A⁰ K) := MulPosMono.toMulPosStrictMono + noncomputable instance Ideal.cancelCommMonoidWithZero : CancelCommMonoidWithZero (Ideal A) := { Function.Injective.cancelCommMonoidWithZero (coeIdealHom A⁰ (FractionRing A)) coeIdeal_injective (RingHom.map_zero _) (RingHom.map_one _) (RingHom.map_mul _) (RingHom.map_pow _) with } -instance Ideal.isDomain : IsDomain (Ideal A) := { } +instance Ideal.isDomain : IsDomain (Ideal A) where + +instance : PosMulReflectLE (Ideal A) where + elim I J K e := by + dsimp + rwa [← FractionalIdeal.coeIdeal_le_coeIdeal (FractionRing A), + ← FractionalIdeal.mul_left_le_iff (J := I) (by simpa using I.2.ne'), + ← FractionalIdeal.coeIdeal_mul, ← FractionalIdeal.coeIdeal_mul, + FractionalIdeal.coeIdeal_le_coeIdeal] + +instance : PosMulStrictMono (Ideal A) := PosMulMono.toPosMulStrictMono + +instance : MulPosStrictMono (Ideal A) := MulPosMono.toMulPosStrictMono /-- For ideals in a Dedekind domain, to divide is to contain. -/ theorem Ideal.dvd_iff_le {I J : Ideal A} : I ∣ J ↔ J ≤ I := @@ -795,6 +827,38 @@ theorem Ideal.exist_integer_multiples_notMem {J : Ideal A} (hJ : J ≠ ⊤) {ι @[deprecated (since := "2025-05-23")] alias Ideal.exist_integer_multiples_not_mem := Ideal.exist_integer_multiples_notMem +lemma Ideal.mul_iInf (I : Ideal A) {ι : Type*} [Nonempty ι] (J : ι → Ideal A) : + I * ⨅ i, J i = ⨅ i, I * J i := by + by_cases hI : I = 0 + · simp [hI] + refine (le_iInf fun i ↦ Ideal.mul_mono_right (iInf_le _ _)).antisymm ?_ + have H : ⨅ i, I * J i ≤ I := (iInf_le _ (Nonempty.some ‹_›)).trans Ideal.mul_le_right + obtain ⟨K, hK⟩ := Ideal.dvd_iff_le.mpr H + rw [hK] + refine mul_le_mul_left' ?_ I + rw [le_iInf_iff] + intro i + rw [← mul_le_mul_iff_of_pos_left (a := I), ← hK] + · exact iInf_le _ _ + · exact bot_lt_iff_ne_bot.mpr hI + +lemma Ideal.iInf_mul (I : Ideal A) {ι : Type*} [Nonempty ι] (J : ι → Ideal A) : + (⨅ i, J i) * I = ⨅ i, J i * I := by + simp only [mul_iInf, mul_comm _ I] + +lemma Ideal.mul_inf (I J K : Ideal A) : I * (J ⊓ K) = I * J ⊓ I * K := by + rw [inf_eq_iInf, Ideal.mul_iInf, inf_eq_iInf] + congr! 2 with ⟨⟩ + +lemma Ideal.inf_mul (I J K : Ideal A) : (I ⊓ J) * K = I * K ⊓ J * K := by + simp only [Ideal.mul_inf, mul_comm _ K] + +lemma FractionalIdeal.mul_inf (I J K : FractionalIdeal A⁰ K) : I * (J ⊓ K) = I * J ⊓ I * K := + mul_inf₀ (zero_le _) _ _ + +lemma FractionalIdeal.inf_mul (I J K : FractionalIdeal A⁰ K) : (I ⊓ J) * K = I * K ⊓ J * K := + inf_mul₀ (zero_le _) _ _ + section Gcd namespace Ideal @@ -858,6 +922,18 @@ theorem factors_span_eq {p : K[X]} : factors (span {p}) = (factors p).map (fun q rw [← span_singleton_eq_span_singleton.mpr (factors_prod hp), ← multiset_prod_span_singleton, factors_eq_normalizedFactors, normalizedFactors_prod_of_prime this] +lemma _root_.FractionalIdeal.sup_mul_inf (I J : FractionalIdeal A⁰ K) : + (I ⊓ J) * (I ⊔ J) = I * J := by + apply mul_left_injective₀ (b := spanSingleton A⁰ (algebraMap A K + (I.den.1 * I.den.1 * J.den.1 * J.den.1))) (by simp [spanSingleton_eq_zero_iff]) + have := Ideal.sup_mul_inf (Ideal.span {J.den.1} * I.num) (Ideal.span {I.den.1} * J.num) + simp only [← coeIdeal_inj (K := K), coeIdeal_mul, coeIdeal_sup, coeIdeal_inf, + ← den_mul_self_eq_num', coeIdeal_span_singleton] at this + rw [mul_left_comm, ← mul_add, ← mul_add, ← mul_inf₀ (FractionalIdeal.zero_le _), + ← mul_inf₀ (FractionalIdeal.zero_le _)] at this + simp only [FractionalIdeal.sup_eq_add, _root_.map_mul, ← spanSingleton_mul_spanSingleton] + convert this using 1 <;> ring + end Ideal end Gcd @@ -990,8 +1066,17 @@ def equivMaximalSpectrum (hR : ¬IsField R) : HeightOneSpectrum R ≃ MaximalSpe toFun v := ⟨v.asIdeal, v.isPrime.isMaximal v.ne_bot⟩ invFun v := ⟨v.asIdeal, v.isMaximal.isPrime, Ring.ne_bot_of_isMaximal_of_not_isField v.isMaximal hR⟩ - left_inv := fun ⟨_, _, _⟩ => rfl - right_inv := fun ⟨_, _⟩ => rfl + +/-- An ideal of `R` is not the whole ring if and only if it is contained in an element of +`HeightOneSpectrum R` -/ +theorem ideal_ne_top_iff_exists (hR : ¬IsField R) (I : Ideal R) : I ≠ ⊤ ↔ + ∃ P : HeightOneSpectrum R, I ≤ P.asIdeal := by + rw [Ideal.ne_top_iff_exists_maximal] + constructor + · rintro ⟨M, hMmax, hIM⟩ + exact ⟨(equivMaximalSpectrum hR).symm ⟨M, hMmax⟩, hIM⟩ + · rintro ⟨P, hP⟩ + exact ⟨((equivMaximalSpectrum hR) P).asIdeal, ((equivMaximalSpectrum hR) P).isMaximal, hP⟩ variable (R) diff --git a/Mathlib/RingTheory/DedekindDomain/SInteger.lean b/Mathlib/RingTheory/DedekindDomain/SInteger.lean index 46495bc7396a6e..bf62cbf41e2ec3 100644 --- a/Mathlib/RingTheory/DedekindDomain/SInteger.lean +++ b/Mathlib/RingTheory/DedekindDomain/SInteger.lean @@ -132,8 +132,6 @@ def unitEquivUnitsInteger : S.unit K ≃* (S.integer K)ˣ where -- Porting note: was -- rw [← map_mul]; convert v.valuation.map_one; exact subtype.mk_eq_mk.mp x.val_inv⟩ rw [Units.val_mk0, ← map_mul, Subtype.mk_eq_mk.mp x.val_inv, map_one]⟩ - left_inv _ := by ext; rfl - right_inv _ := by ext; rfl map_mul' _ _ := by ext; rfl end Set diff --git a/Mathlib/RingTheory/FractionalIdeal/Basic.lean b/Mathlib/RingTheory/FractionalIdeal/Basic.lean index f4c9365ebdfc1c..769d9a66085fef 100644 --- a/Mathlib/RingTheory/FractionalIdeal/Basic.lean +++ b/Mathlib/RingTheory/FractionalIdeal/Basic.lean @@ -400,7 +400,6 @@ instance orderBot : OrderBot (FractionalIdeal S P) where theorem bot_eq_zero : (⊥ : FractionalIdeal S P) = 0 := rfl -@[simp] theorem le_zero_iff {I : FractionalIdeal S P} : I ≤ 0 ↔ I = 0 := le_bot_iff @@ -466,6 +465,12 @@ theorem mem_add (I J : FractionalIdeal S P) (x : P) : x ∈ I + J ↔ ∃ i ∈ I, ∃ j ∈ J, i + j = x := by rw [← mem_coe, coe_add, Submodule.add_eq_sup]; exact Submodule.mem_sup +@[simp, norm_cast] +lemma coeIdeal_inf [FaithfulSMul R P] (I J : Ideal R) : + (↑(I ⊓ J) : FractionalIdeal S P) = ↑I ⊓ ↑J := by + apply coeToSubmodule_injective + exact Submodule.map_inf (Algebra.linearMap R P) (FaithfulSMul.algebraMap_injective R P) + @[simp, norm_cast] theorem coeIdeal_sup (I J : Ideal R) : ↑(I ⊔ J) = (I + J : FractionalIdeal S P) := coeToSubmodule_injective <| coeSubmodule_sup _ _ _ @@ -581,6 +586,13 @@ instance commSemiring : CommSemiring (FractionalIdeal S P) := Function.Injective.commSemiring _ Subtype.coe_injective coe_zero coe_one coe_add coe_mul (fun _ _ => coe_nsmul _ _) coe_pow coe_natCast +instance : CanonicallyOrderedAdd (FractionalIdeal S P) where + exists_add_of_le h := ⟨_, (sup_eq_right.mpr h).symm⟩ + le_self_add _ _ := le_sup_left + +instance : IsOrderedRing (FractionalIdeal S P) := + CanonicallyOrderedAdd.toIsOrderedRing + end Semiring variable (S P) diff --git a/Mathlib/RingTheory/HahnSeries/Addition.lean b/Mathlib/RingTheory/HahnSeries/Addition.lean index 28e53c8a0133ef..dedbf43c09137a 100644 --- a/Mathlib/RingTheory/HahnSeries/Addition.lean +++ b/Mathlib/RingTheory/HahnSeries/Addition.lean @@ -100,6 +100,10 @@ theorem coeff_add' (x y : HahnSeries Γ R) : (x + y).coeff = x.coeff + y.coeff : theorem coeff_add {x y : HahnSeries Γ R} {a : Γ} : (x + y).coeff a = x.coeff a + y.coeff a := rfl +@[simp] theorem single_add (a : Γ) (r s : R) : single a (r + s) = single a r + single a s := by + classical + ext : 1; exact Pi.single_add (f := fun _ => R) a r s + @[deprecated (since := "2025-01-31")] alias add_coeff := coeff_add instance : AddMonoid (HahnSeries Γ R) := fast_instance% @@ -287,9 +291,7 @@ theorem order_lt_order_of_eq_add_single {R} {Γ} [LinearOrder Γ] [Zero Γ] [Add @[simps!] def single.addMonoidHom (a : Γ) : R →+ HahnSeries Γ R := { single a with - map_add' := fun x y => by - ext b - by_cases h : b = a <;> simp [h] } + map_add' := single_add _ } /-- `coeff g` as an additive monoid/group homomorphism -/ @[simps] @@ -353,6 +355,8 @@ theorem coeff_neg' (x : HahnSeries Γ R) : (-x).coeff = -x.coeff := theorem coeff_neg {x : HahnSeries Γ R} {a : Γ} : (-x).coeff a = -x.coeff a := rfl +@[deprecated (since := "2025-01-31")] alias neg_coeff := coeff_neg + instance : Sub (HahnSeries Γ R) where sub x y := { coeff := x.coeff - y.coeff @@ -374,7 +378,13 @@ instance : AddGroup (HahnSeries Γ R) := fast_instance% coeff_zero' coeff_add' (coeff_neg') (coeff_sub') (fun _ _ => coeff_smul' _ _) (fun _ _ => coeff_smul' _ _) -@[deprecated (since := "2025-01-31")] alias neg_coeff := coeff_neg +@[simp] +theorem single_sub (a : Γ) (r s : R) : single a (r - s) = single a r - single a s := + map_sub (single.addMonoidHom a) _ _ + +@[simp] +theorem single_neg (a : Γ) (r : R) : single a (-r) = -single a r := + map_neg (single.addMonoidHom a) _ @[simp] theorem support_neg {x : HahnSeries Γ R} : (-x).support = x.support := by diff --git a/Mathlib/RingTheory/HahnSeries/Multiplication.lean b/Mathlib/RingTheory/HahnSeries/Multiplication.lean index 7da1a02e1df395..1a707f08ae6a3b 100644 --- a/Mathlib/RingTheory/HahnSeries/Multiplication.lean +++ b/Mathlib/RingTheory/HahnSeries/Multiplication.lean @@ -54,8 +54,11 @@ namespace HahnSeries variable [Zero Γ] [PartialOrder Γ] -instance [Zero R] [One R] : One (HahnSeries Γ R) := - ⟨single 0 1⟩ +instance [Zero R] [One R] : One (HahnSeries Γ R) where one := single 0 1 +instance [Zero R] [NatCast R] : NatCast (HahnSeries Γ R) where natCast n := single 0 n +instance [Zero R] [IntCast R] : IntCast (HahnSeries Γ R) where intCast z := single 0 z +instance [Zero R] [NNRatCast R] : NNRatCast (HahnSeries Γ R) where nnratCast q := single 0 q +instance [Zero R] [RatCast R] : RatCast (HahnSeries Γ R) where ratCast q := single 0 q open Classical in @[simp] @@ -65,9 +68,14 @@ theorem coeff_one [Zero R] [One R] {a : Γ} : @[deprecated (since := "2025-01-31")] alias one_coeff := coeff_one -@[simp] -theorem single_zero_one [Zero R] [One R] : single (0 : Γ) (1 : R) = 1 := - rfl +@[simp] theorem single_zero_one [Zero R] [One R] : single (0 : Γ) (1 : R) = 1 := rfl +theorem single_zero_natCast [Zero R] [NatCast R] (n : ℕ) : single (0 : Γ) (n : R) = n := rfl +theorem single_zero_intCast [Zero R] [IntCast R] (z : ℤ) : single (0 : Γ) (z : R) = z := rfl +theorem single_zero_nnratCast [Zero R] [NNRatCast R] (q : ℚ≥0) : single (0 : Γ) (q : R) = q := rfl +theorem single_zero_ratCast [Zero R] [RatCast R] (q : ℚ) : single (0 : Γ) (q : R) = q := rfl + +theorem single_zero_ofNat [Zero R] [NatCast R] (n : ℕ) [n.AtLeastTwo] : + single (0 : Γ) (ofNat(n) : R) = ofNat(n) := rfl @[simp] theorem support_one [MulZeroOneClass R] [Nontrivial R] : support (1 : HahnSeries Γ R) = {0} := @@ -89,9 +97,16 @@ theorem leadingCoeff_one [MulZeroOneClass R] : (1 : HahnSeries Γ R).leadingCoef @[simp] protected lemma map_one [MonoidWithZero R] [MonoidWithZero S] (f : R →*₀ S) : - (1 : HahnSeries Γ R).map f = (1 : HahnSeries Γ S) := by - ext g - by_cases h : g = 0 <;> simp [h] + (1 : HahnSeries Γ R).map f = (1 : HahnSeries Γ S) := + HahnSeries.map_single (a := (0 : Γ)) f.toZeroHom |>.trans <| congrArg _ <| f.map_one + +instance [AddCommMonoidWithOne R] : AddCommMonoidWithOne (HahnSeries Γ R) where + natCast_zero := by simp [← single_zero_natCast] + natCast_succ n := by simp [← single_zero_natCast] + +instance [AddCommGroupWithOne R] : AddCommGroupWithOne (HahnSeries Γ R) where + intCast_ofNat n := by simp [← single_zero_natCast, ← single_zero_intCast] + intCast_negSucc n := by simp [← single_zero_natCast, ← single_zero_intCast] end HahnSeries @@ -557,7 +572,7 @@ instance [NonUnitalSemiring R] : NonUnitalSemiring (HahnSeries Γ R) := mul_assoc := mul_assoc' } instance [NonAssocSemiring R] : NonAssocSemiring (HahnSeries Γ R) := - { AddMonoidWithOne.unary, + { inferInstanceAs (AddMonoidWithOne (HahnSeries Γ R)), inferInstanceAs (NonUnitalNonAssocSemiring (HahnSeries Γ R)) with one_mul := fun x => by ext @@ -591,11 +606,12 @@ instance [NonUnitalRing R] : NonUnitalRing (HahnSeries Γ R) := instance [NonAssocRing R] : NonAssocRing (HahnSeries Γ R) := { inferInstanceAs (NonUnitalNonAssocRing (HahnSeries Γ R)), - inferInstanceAs (NonAssocSemiring (HahnSeries Γ R)) with } + inferInstanceAs (NonAssocSemiring (HahnSeries Γ R)), + inferInstanceAs (AddGroupWithOne (HahnSeries Γ R)) with } instance [Ring R] : Ring (HahnSeries Γ R) := { inferInstanceAs (Semiring (HahnSeries Γ R)), - inferInstanceAs (AddCommGroup (HahnSeries Γ R)) with } + inferInstanceAs (AddCommGroupWithOne (HahnSeries Γ R)) with } instance [NonUnitalCommRing R] : NonUnitalCommRing (HahnSeries Γ R) := { inferInstanceAs (NonUnitalCommSemiring (HahnSeries Γ R)), diff --git a/Mathlib/RingTheory/HahnSeries/Summable.lean b/Mathlib/RingTheory/HahnSeries/Summable.lean index 59652f66d22e9d..917511b2a35bb5 100644 --- a/Mathlib/RingTheory/HahnSeries/Summable.lean +++ b/Mathlib/RingTheory/HahnSeries/Summable.lean @@ -3,7 +3,9 @@ Copyright (c) 2021 Aaron Anderson. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Aaron Anderson -/ +import Mathlib.Algebra.Ring.Action.Rat import Mathlib.RingTheory.HahnSeries.Multiplication +import Mathlib.Data.Rat.Cast.Lemmas /-! # Summable families of Hahn Series @@ -832,7 +834,6 @@ theorem single_div_single (a b : Γ) (r s : R) : rw [div_eq_mul_inv, sub_eq_add_neg, div_eq_mul_inv, inv_single, single_mul_single] instance instField : Field (HahnSeries Γ R) where - __ : IsDomain (HahnSeries Γ R) := inferInstance inv_zero := by simp [inv_def] mul_inv_cancel x x0 := by have h := @@ -840,11 +841,21 @@ instance instField : Field (HahnSeries Γ R) where (unit_aux x (inv_mul_cancel₀ (leadingCoeff_ne_iff.mpr x0)) _ (neg_add_cancel x.order)) rw [sub_sub_cancel] at h rw [inv_def, ← mul_assoc, mul_comm x, h] - -- TODO: use `(· • ·)` here to avoid diamonds - nnqsmul := _ - nnqsmul_def := fun _ _ => rfl - qsmul := _ - qsmul_def := fun _ _ => rfl + nnqsmul := (· • ·) + qsmul := (· • ·) + nnqsmul_def q x := by ext; simp [← single_zero_nnratCast, NNRat.smul_def] + qsmul_def q x := by ext; simp [← single_zero_ratCast, Rat.smul_def] + nnratCast_def q := by + simp [← single_zero_nnratCast, ← single_zero_natCast, NNRat.cast_def] + ratCast_def q := by + simp [← single_zero_ratCast, ← single_zero_intCast, ← single_zero_natCast, Rat.cast_def] + +example : (instSMul : SMul NNRat (HahnSeries Γ R)) = NNRat.smulDivisionSemiring := rfl +example : (instSMul : SMul ℚ (HahnSeries Γ R)) = Rat.smulDivisionRing := rfl + +theorem single_zero_ofScientific (m e s) : + single (0 : Γ) (OfScientific.ofScientific m e s : R) = OfScientific.ofScientific m e s := by + simpa using single_zero_ratCast (Γ := Γ) (R := R) (OfScientific.ofScientific m e s) end Field diff --git a/Mathlib/RingTheory/Ideal/Maximal.lean b/Mathlib/RingTheory/Ideal/Maximal.lean index 51cba1c7452d71..d14006ac3e3c60 100644 --- a/Mathlib/RingTheory/Ideal/Maximal.lean +++ b/Mathlib/RingTheory/Ideal/Maximal.lean @@ -76,6 +76,13 @@ theorem exists_maximal [Nontrivial α] : ∃ M : Ideal α, M.IsMaximal := let ⟨I, ⟨hI, _⟩⟩ := exists_le_maximal (⊥ : Ideal α) bot_ne_top ⟨I, hI⟩ +theorem ne_top_iff_exists_maximal {I : Ideal α} : I ≠ ⊤ ↔ ∃ M : Ideal α, M.IsMaximal ∧ I ≤ M := by + refine ⟨exists_le_maximal I, ?_⟩ + contrapose! + rintro rfl _ hMmax + rw [top_le_iff] + exact IsMaximal.ne_top hMmax + instance [Nontrivial α] : Nontrivial (Ideal α) := by rcases@exists_maximal α _ _ with ⟨M, hM, _⟩ exact nontrivial_of_ne M ⊤ hM diff --git a/Mathlib/RingTheory/Ideal/Operations.lean b/Mathlib/RingTheory/Ideal/Operations.lean index 499b320f9e6595..21eb9b285d1eb7 100644 --- a/Mathlib/RingTheory/Ideal/Operations.lean +++ b/Mathlib/RingTheory/Ideal/Operations.lean @@ -307,6 +307,14 @@ theorem mul_sup : I * (J ⊔ K) = I * J ⊔ I * K := theorem sup_mul : (I ⊔ J) * K = I * K ⊔ J * K := Submodule.sup_smul I J K +theorem mul_iSup {ι : Sort*} (J : ι → Ideal R) : + I * (⨆ i, J i) = ⨆ i, I * J i := + Submodule.smul_iSup + +theorem iSup_mul {ι : Sort*} (J : ι → Ideal R) (I : Ideal R) : + (⨆ i, J i) * I = ⨆ i, J i * I := + Submodule.iSup_smul + variable {I J K} theorem pow_le_pow_right {m n : ℕ} (h : m ≤ n) : I ^ n ≤ I ^ m := by diff --git a/Mathlib/RingTheory/KrullDimension/Basic.lean b/Mathlib/RingTheory/KrullDimension/Basic.lean index b9249141310344..dd061115a8f1f9 100644 --- a/Mathlib/RingTheory/KrullDimension/Basic.lean +++ b/Mathlib/RingTheory/KrullDimension/Basic.lean @@ -125,10 +125,9 @@ theorem nilradical_le_jacobson (R) [CommRing R] : nilradical R ≤ Ring.jacobson nilradical_eq_sInf R ▸ le_sInf fun _I hI ↦ sInf_le (Ideal.IsMaximal.isPrime ⟨hI⟩) theorem Ring.jacobson_eq_nilradical_of_krullDimLE_zero (R) [CommRing R] [KrullDimLE 0 R] : - jacobson R = nilradical R := by - refine (nilradical_le_jacobson R).antisymm' (nilradical_eq_sInf R ▸ le_sInf fun I hI ↦ sInf_le ?_) - rw [Set.mem_def, Set.setOf_app_iff] at hI - exact Ideal.IsMaximal.out + jacobson R = nilradical R := + (nilradical_le_jacobson R).antisymm' <| nilradical_eq_sInf R ▸ le_sInf fun I (_ : I.IsPrime) ↦ + sInf_le Ideal.IsMaximal.out end Zero diff --git a/Mathlib/RingTheory/LittleWedderburn.lean b/Mathlib/RingTheory/LittleWedderburn.lean index 249dad251725d1..17c46a7aad2ccd 100644 --- a/Mathlib/RingTheory/LittleWedderburn.lean +++ b/Mathlib/RingTheory/LittleWedderburn.lean @@ -125,7 +125,7 @@ private theorem center_eq_top [Finite D] (hD : InductionHyp D) : Subring.center have aux : ∀ {k : ℕ}, ((X : ℤ[X]) ^ k - 1).eval ↑q = (q : ℤ) ^ k - 1 := by simp only [eval_X, eval_one, eval_pow, eval_sub, eq_self_iff_true, forall_const] rw [← aux, ← aux, ← eval_mul] - refine (evalRingHom ↑q).map_dvd (X_pow_sub_one_mul_cyclotomic_dvd_X_pow_sub_one_of_dvd ℤ ?_) + refine map_dvd (evalRingHom ↑q) (X_pow_sub_one_mul_cyclotomic_dvd_X_pow_sub_one_of_dvd ℤ ?_) refine Nat.mem_properDivisors.mpr ⟨⟨_, (finrank_mul_finrank Z Zx D).symm⟩, ?_⟩ rw [← Nat.pow_lt_pow_iff_right hq, ← card_D, ← card_Zx] obtain ⟨b, -, hb⟩ := SetLike.exists_of_lt hZx.lt_top diff --git a/Mathlib/RingTheory/Localization/FractionRing.lean b/Mathlib/RingTheory/Localization/FractionRing.lean index d7bfae6bbb41fd..b7fb98ae89b8f5 100644 --- a/Mathlib/RingTheory/Localization/FractionRing.lean +++ b/Mathlib/RingTheory/Localization/FractionRing.lean @@ -3,6 +3,7 @@ Copyright (c) 2018 Kenny Lau. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Kenny Lau, Mario Carneiro, Johan Commelin, Amelia Livingston, Anne Baanen -/ +import Mathlib.Algebra.Ring.Hom.InjSurj import Mathlib.Algebra.Field.Equiv import Mathlib.Algebra.Field.Subfield.Basic import Mathlib.Algebra.Order.Ring.Int diff --git a/Mathlib/RingTheory/MatrixAlgebra.lean b/Mathlib/RingTheory/MatrixAlgebra.lean index 809ffe33a6ac13..1ebb1f63afd7f7 100644 --- a/Mathlib/RingTheory/MatrixAlgebra.lean +++ b/Mathlib/RingTheory/MatrixAlgebra.lean @@ -16,36 +16,37 @@ import Mathlib.RingTheory.TensorProduct.Basic * `matrixEquivTensor : Matrix n n A ≃ₐ[R] (A ⊗[R] Matrix n n R)`. * `Matrix.kroneckerTMulAlgEquiv : - Matrix m m A ⊗[R] Matrix n n B ≃ₐ[R] Matrix (m × n) (m × n) (A ⊗[R] B)`, - where the forward map is the (tensor-ified) kronecker product. + Matrix m m A ⊗[R] Matrix n n B ≃ₐ[S] Matrix (m × n) (m × n) (A ⊗[R] B)`, + where the forward map is the (tensor-ified) Kronecker product. -/ suppress_compilation open TensorProduct Algebra.TensorProduct Matrix -variable {l m n p : Type*} {R A B M N : Type*} +variable {l m n p : Type*} {R S A B M N : Type*} section Module -variable [CommSemiring R] [AddCommMonoid M] [AddCommMonoid N] [Semiring A] [Semiring B] -variable [Module R M] [Module R N] [Algebra R A] [Algebra R B] +variable [CommSemiring R] [Semiring S] [Semiring A] [Semiring B] [AddCommMonoid M] [AddCommMonoid N] +variable [Algebra R S] [Algebra R A] [Algebra R B] [Module R M] [Module S M] [Module R N] +variable [IsScalarTower R S M] variable [Fintype l] [Fintype m] [Fintype n] [Fintype p] variable [DecidableEq l] [DecidableEq m] [DecidableEq n] [DecidableEq p] open Kronecker -variable (l m n p R M N) +variable (l m n p R S A M N) attribute [local ext] ext_linearMap /-- `Matrix.kroneckerTMul` as a linear equivalence, when the two arguments are tensored. -/ def kroneckerTMulLinearEquiv : - Matrix l m M ⊗[R] Matrix n p N ≃ₗ[R] Matrix (l × n) (m × p) (M ⊗[R] N) := + Matrix l m M ⊗[R] Matrix n p N ≃ₗ[S] Matrix (l × n) (m × p) (M ⊗[R] N) := .ofLinear - (TensorProduct.lift <| kroneckerTMulBilinear _) - ((LinearMap.lsum R _ R fun ii => LinearMap.lsum R _ R fun jj => TensorProduct.map - (singleLinearMap R ii.1 jj.1) (singleLinearMap R ii.2 jj.2)) - ∘ₗ (ofLinearEquiv R).symm.toLinearMap) + (AlgebraTensorModule.lift <| kroneckerTMulBilinear R S) + ((LinearMap.lsum S _ R fun ii => LinearMap.lsum S _ R fun jj => AlgebraTensorModule.map + (singleLinearMap S ii.1 jj.1) (singleLinearMap R ii.2 jj.2)) + ∘ₗ (ofLinearEquiv S).symm.toLinearMap) (by ext : 4 simp [-LinearMap.lsum_apply, LinearMap.lsum_piSingle, @@ -57,12 +58,12 @@ def kroneckerTMulLinearEquiv : @[simp] theorem kroneckerTMulLinearEquiv_tmul (a : Matrix l m M) (b : Matrix n p N) : - kroneckerTMulLinearEquiv l m n p R M N (a ⊗ₜ b) = a ⊗ₖₜ b := rfl + kroneckerTMulLinearEquiv l m n p R S M N (a ⊗ₜ b) = a ⊗ₖₜ b := rfl @[simp] theorem kroneckerTMulAlgEquiv_symm_single_tmul (ia : l) (ja : m) (ib : n) (jb : p) (a : M) (b : N) : - (kroneckerTMulLinearEquiv l m n p R M N).symm (single (ia, ib) (ja, jb) (a ⊗ₜ b)) = + (kroneckerTMulLinearEquiv l m n p R S M N).symm (single (ia, ib) (ja, jb) (a ⊗ₜ b)) = single ia ja a ⊗ₜ single ib jb b := by rw [LinearEquiv.symm_apply_eq, kroneckerTMulLinearEquiv_tmul, single_kroneckerTMul_single] @@ -71,17 +72,17 @@ theorem kroneckerTMulAlgEquiv_symm_single_tmul alias kroneckerTMulAlgEquiv_symm_stdBasisMatrix_tmul := kroneckerTMulAlgEquiv_symm_single_tmul @[simp] -theorem kroneckerTMulLinearEquiv_one : - kroneckerTMulLinearEquiv m m n n R A B 1 = 1 := by simp [Algebra.TensorProduct.one_def] +theorem kroneckerTMulLinearEquiv_one [Module S A] [IsScalarTower R S A] : + kroneckerTMulLinearEquiv m m n n R S A B 1 = 1 := by simp [Algebra.TensorProduct.one_def] /-- Note this can't be stated for rectangular matrices because there is no `HMul (TensorProduct R _ _) (TensorProduct R _ _) (TensorProduct R _ _)` instance. -/ @[simp] -theorem kroneckerTMulLinearEquiv_mul : +theorem kroneckerTMulLinearEquiv_mul [Module S A] [IsScalarTower R S A] : ∀ x y : Matrix m m A ⊗[R] Matrix n n B, - kroneckerTMulLinearEquiv m m n n R A B (x * y) = - kroneckerTMulLinearEquiv m m n n R A B x * kroneckerTMulLinearEquiv m m n n R A B y := - (kroneckerTMulLinearEquiv m m n n R A B).toLinearMap.map_mul_iff.2 <| by + kroneckerTMulLinearEquiv m m n n R S A B (x * y) = + kroneckerTMulLinearEquiv m m n n R S A B x * kroneckerTMulLinearEquiv m m n n R S A B y := + (kroneckerTMulLinearEquiv m m n n R S A B).toLinearMap.restrictScalars R |>.map_mul_iff.2 <| by ext : 10 simp [single_kroneckerTMul_single, mul_kroneckerTMul_mul] @@ -223,25 +224,28 @@ theorem matrixEquivTensor_apply_symm (a : A) (M : Matrix n n R) : namespace Matrix open scoped Kronecker -variable (m) (B) [Fintype m] [DecidableEq m] +variable (m) (S B) +variable [CommSemiring S] [Algebra R S] [Algebra S A] [IsScalarTower R S A] +variable [Fintype m] [DecidableEq m] /-- `Matrix.kroneckerTMul` as an algebra equivalence, when the two arguments are tensored. -/ def kroneckerTMulAlgEquiv : - Matrix m m A ⊗[R] Matrix n n B ≃ₐ[R] Matrix (m × n) (m × n) (A ⊗[R] B) := - .ofLinearEquiv (kroneckerTMulLinearEquiv m m n n R A B) - (kroneckerTMulLinearEquiv_one _ _ _) - (kroneckerTMulLinearEquiv_mul _ _ _) + Matrix m m A ⊗[R] Matrix n n B ≃ₐ[S] Matrix (m × n) (m × n) (A ⊗[R] B) := + .ofLinearEquiv (kroneckerTMulLinearEquiv m m n n R S A B) + (kroneckerTMulLinearEquiv_one _ _ _ _ _) + (kroneckerTMulLinearEquiv_mul _ _ _ _ _) variable {m n A B} @[simp] theorem kroneckerTMulAlgEquiv_apply (x : Matrix m m A ⊗[R] Matrix n n B) : - (kroneckerTMulAlgEquiv m n R A B) x = kroneckerTMulLinearEquiv m m n n R A B x := + (kroneckerTMulAlgEquiv m n R S A B) x = kroneckerTMulLinearEquiv m m n n R S A B x := rfl @[simp] theorem kroneckerTMulAlgEquiv_symm_apply (x : Matrix (m × n) (m × n) (A ⊗[R] B)) : - (kroneckerTMulAlgEquiv m n R A B).symm x = (kroneckerTMulLinearEquiv m m n n R A B).symm x := + (kroneckerTMulAlgEquiv m n R S A B).symm x = + (kroneckerTMulLinearEquiv m m n n R S A B).symm x := rfl end Matrix diff --git a/Mathlib/RingTheory/Perfection.lean b/Mathlib/RingTheory/Perfection.lean index a159eba199e78c..954f7162d6632e 100644 --- a/Mathlib/RingTheory/Perfection.lean +++ b/Mathlib/RingTheory/Perfection.lean @@ -173,7 +173,6 @@ noncomputable def lift (R : Type u₁) [CommSemiring R] [CharP R p] [PerfectRing map_add' := fun _ _ => ext fun _ => (congr_arg f <| iterate_map_add _ _ _ _).trans <| f.map_add _ _ } invFun := RingHom.comp <| coeff S p 0 - left_inv _ := RingHom.ext fun _ => rfl right_inv f := RingHom.ext fun r => ext fun n => show coeff S p 0 (f (((frobeniusEquiv R p).symm)^[n] r)) = coeff S p n (f r) by rw [← coeff_iterate_frobenius _ 0 n, zero_add, ← RingHom.map_iterate_frobenius, diff --git a/Mathlib/RingTheory/Polynomial/Basic.lean b/Mathlib/RingTheory/Polynomial/Basic.lean index f43c27b0290896..cc7fa3cb98b7c5 100644 --- a/Mathlib/RingTheory/Polynomial/Basic.lean +++ b/Mathlib/RingTheory/Polynomial/Basic.lean @@ -786,10 +786,12 @@ theorem prime_C_iff : Prime (C r : MvPolynomial σ R) ↔ Prime r := obtain ⟨s, a', b', rfl, rfl⟩ := exists_finset_rename₂ a b rw [← algebraMap_eq] at hd have : algebraMap R _ r ∣ a' * b' := by - convert killCompl Subtype.coe_injective |>.toRingHom.map_dvd hd <;> simp + convert _root_.map_dvd (killCompl Subtype.val_injective) hd + · simp + · simp rw [← rename_C ((↑) : s → σ)] let f := (rename (R := R) ((↑) : s → σ)).toRingHom - exact (((prime_C_iff_of_fintype s).2 hr).2.2 a' b' this).imp f.map_dvd f.map_dvd⟩⟩ + exact (((prime_C_iff_of_fintype s).2 hr).2.2 a' b' this).imp (map_dvd f) (map_dvd f)⟩⟩ variable {σ} diff --git a/Mathlib/RingTheory/Polynomial/Content.lean b/Mathlib/RingTheory/Polynomial/Content.lean index 2b0c0c43653333..f602a176af968f 100644 --- a/Mathlib/RingTheory/Polynomial/Content.lean +++ b/Mathlib/RingTheory/Polynomial/Content.lean @@ -430,7 +430,7 @@ theorem dvd_iff_content_dvd_content_and_primPart_dvd_primPart {p q : R[X]} (hq : rw [content_mul, p.isPrimitive_primPart.dvd_primPart_iff_dvd hq] exact ⟨Dvd.intro _ rfl, p.primPart_dvd.trans (Dvd.intro _ rfl)⟩ · rw [p.eq_C_content_mul_primPart, q.eq_C_content_mul_primPart] - exact mul_dvd_mul (RingHom.map_dvd C h.1) h.2 + exact mul_dvd_mul (_root_.map_dvd C h.1) h.2 noncomputable instance (priority := 100) normalizedGcdMonoid : NormalizedGCDMonoid R[X] := letI := Classical.decEq R diff --git a/Mathlib/RingTheory/Polynomial/ContentIdeal.lean b/Mathlib/RingTheory/Polynomial/ContentIdeal.lean new file mode 100644 index 00000000000000..dbfd0344167e0a --- /dev/null +++ b/Mathlib/RingTheory/Polynomial/ContentIdeal.lean @@ -0,0 +1,168 @@ +/- +Copyright (c) 2025 Fabrizio Barroero. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Fabrizio Barroero +-/ + +import Mathlib.Order.CompletePartialOrder +import Mathlib.RingTheory.Ideal.BigOperators +import Mathlib.RingTheory.Polynomial.Content + +/-! +# The content ideal of a polynomial + +In this file we introduce the content ideal of a polynomial `p : R[X]` as the ideal generated by its +coefficients, and we prove some basic properties about it. + +## Main Definitions +Let `p : R[X]`. +- `p.contentIdeal` is the `Ideal R` generated by the coefficients of `p`. + +## Main Results +- `Polynomial.isPrimitive_of_contentIdeal_eq_top`: if the content ideal of `p` is the whole ring, + then `p` is primitive. +- `Submodule.IsPrincipal.isPrimitive_iff_contentIdeal_eq_top`: in case the content ideal of `p` is + principal, `p` is primitive if and only if its content ideal is the whole ring. +- `Submodule.IsPrincipal.contentIdeal_eq_span_content_of_isPrincipal`: if the content ideal of `p` + is principal, then it is equal to the ideal generated by the content of `p`. + +## TODO + +- Prove the Dedekind-Mertens lemma, see https://www.cse.chalmers.se/~coquand/mertens.pdf + +-/ + +namespace Polynomial + +open Ideal + +variable {R : Type*} [Semiring R] (p : R[X]) + +/-- The content ideal of a polynomial `p` is the ideal generated by its coefficients. -/ +def contentIdeal := span p.coeffs.toSet + +theorem contentIdeal_def : p.contentIdeal = span p.coeffs.toSet := rfl + +@[simp] +theorem contenIdeal_zero : (0 : R[X]).contentIdeal = ⊥ := by + simp [contentIdeal_def] + +@[simp] +theorem contentIdeal_eq_bot_iff : p.contentIdeal = ⊥ ↔ p = 0 := by + simp only [contentIdeal_def, span_eq_bot] + refine ⟨?_, fun h ↦ by simp [h]⟩ + contrapose! + exact fun h ↦ ⟨p.leadingCoeff, coeff_mem_coeffs p p.natDegree (leadingCoeff_ne_zero.mpr h), + leadingCoeff_ne_zero.mpr h⟩ + +theorem coeff_mem_contentIdeal (n : ℕ) : p.coeff n ∈ p.contentIdeal := by + by_cases h : p.coeff n = 0 + · simp [h] + · apply subset_span + simpa using coeff_mem_coeffs p n h + +@[simp] +theorem contentIdeal_monomial (n : ℕ) (r : R) : (monomial n r).contentIdeal = span {r} := by + by_cases h : r = 0 <;> + simp [h, Set.singleton_zero, contentIdeal_def, coeffs_monomial] + +@[simp] +theorem contentIdeal_C (r : R) : (C r).contentIdeal = span {r} := by + rw [← monomial_zero_left] + exact contentIdeal_monomial 0 r + +theorem contentIdeal_FG : p.contentIdeal.FG := ⟨p.coeffs, rfl⟩ + +section CommSemiring + +variable {R : Type*} [CommSemiring R] {p q : R[X]} + +theorem contentIdeal_le_contentIdeal_of_dvd (hpq : p ∣ q) : + q.contentIdeal ≤ p.contentIdeal := by + rw [contentIdeal_def, span_le] + intro _ h1 + rw [Finset.mem_coe, mem_coeffs_iff] at h1 + obtain ⟨_, _, h2⟩ := h1 + obtain ⟨_, h3⟩ := hpq + rw [h3, coeff_mul] at h2 + rw [h2] + exact Ideal.sum_mem _ <| fun _ _ ↦ mul_mem_right _ _ <| coeff_mem_contentIdeal p _ + +theorem _root_.Submodule.IsPrincipal.contentIdeal_generator_dvd_coeff + (h_prin : p.contentIdeal.IsPrincipal) (n : ℕ) : h_prin.generator ∣ p.coeff n := by + have := p.coeff_mem_contentIdeal n + rw [Submodule.IsPrincipal.mem_iff_eq_smul_generator] at this + obtain ⟨_, ha⟩ := this + simp [ha] + +theorem _root_.Submodule.IsPrincipal.contentIdeal_generator_dvd + (h_prin : p.contentIdeal.IsPrincipal) : C h_prin.generator ∣ p := by + rw [C_dvd_iff_dvd_coeff] + exact fun i ↦ h_prin.contentIdeal_generator_dvd_coeff i + +theorem _root_.Submodule.IsPrincipal.contentIdeal_le_span_iff_dvd + (h_prin : p.contentIdeal.IsPrincipal) (r : R) : + p.contentIdeal ≤ span {r} ↔ C r ∣ p := by + constructor + · rw [← p.contentIdeal.span_singleton_generator] + intro _ + calc + C r ∣ C h_prin.generator := by + apply _root_.map_dvd C + rwa [← span_singleton_le_span_singleton] + _ ∣ p := h_prin.contentIdeal_generator_dvd + · rw [← contentIdeal_C r] + exact fun h ↦ contentIdeal_le_contentIdeal_of_dvd h + +/-- If the coefficients of `p` geneate the whole ring, then `p` is primitive. -/ +theorem isPrimitive_of_contentIdeal_eq_top (h : p.contentIdeal = ⊤) : p.IsPrimitive := by + have h_prin : p.contentIdeal.IsPrincipal := by + rw [h] + exact top_isPrincipal + intro r + simp [← h_prin.contentIdeal_le_span_iff_dvd r, h] + +theorem _root_.Submodule.IsPrincipal.isPrimitive_iff_contentIdeal_eq_top + (h_prin : p.contentIdeal.IsPrincipal) : p.IsPrimitive ↔ p.contentIdeal = ⊤ := by + refine ⟨?_, fun h ↦ isPrimitive_of_contentIdeal_eq_top h⟩ + contrapose! + simp only [IsPrimitive, not_forall] + intro _ + use h_prin.generator, h_prin.contentIdeal_generator_dvd + simp_all [← Ideal.span_singleton_eq_top] + +end CommSemiring + +section NormalizedGCDMonoid + +variable {R : Type*} [CommRing R] [IsDomain R] [NormalizedGCDMonoid R] {p : R[X]} + +theorem contentIdeal_le_span_content : p.contentIdeal ≤ span {p.content} := by + rw [contentIdeal_def, span_le] + intro _ h1 + rw [Finset.mem_coe, mem_coeffs_iff] at h1 + obtain ⟨n, _, h2⟩ := h1 + rw [SetLike.mem_coe, h2, mem_span_singleton] + exact content_dvd_coeff n + +theorem _root_.Submodule.IsPrincipal.contentIdeal_eq_span_content_of_isPrincipal + (h_prin : p.contentIdeal.IsPrincipal) : p.contentIdeal = span {p.content} := by + apply le_antisymm contentIdeal_le_span_content + rw [← p.contentIdeal.span_singleton_generator, span_singleton_le_span_singleton, content, + Finset.dvd_gcd_iff] + exact fun n _ ↦ h_prin.contentIdeal_generator_dvd_coeff n + +end NormalizedGCDMonoid + +section IsBezout + +variable {R : Type*} [CommSemiring R] [IsBezout R] (p : R[X]) + +/-- The polynomial `p` is primitive if and only if the coefficients of `p` geneate the whole ring. +-/ +theorem isPrimitive_iff_contentIdeal_eq_top : p.IsPrimitive ↔ p.contentIdeal = ⊤ := + (IsBezout.isPrincipal_of_FG _ p.contentIdeal_FG).isPrimitive_iff_contentIdeal_eq_top + +end IsBezout + +end Polynomial diff --git a/Mathlib/RingTheory/Polynomial/Cyclotomic/Basic.lean b/Mathlib/RingTheory/Polynomial/Cyclotomic/Basic.lean index 0cf530fedc0564..45f1306e155ed6 100644 --- a/Mathlib/RingTheory/Polynomial/Cyclotomic/Basic.lean +++ b/Mathlib/RingTheory/Polynomial/Cyclotomic/Basic.lean @@ -337,7 +337,7 @@ theorem cyclotomic.dvd_X_pow_sub_one (n : ℕ) (R : Type*) [Ring R] : cyclotomic n R ∣ X ^ n - 1 := by suffices cyclotomic n ℤ ∣ X ^ n - 1 by simpa only [map_cyclotomic_int, Polynomial.map_sub, Polynomial.map_one, Polynomial.map_pow, - Polynomial.map_X] using map_dvd (Int.castRingHom R) this + Polynomial.map_X] using Polynomial.map_dvd (Int.castRingHom R) this rcases n.eq_zero_or_pos with (rfl | hn) · simp rw [← prod_cyclotomic_eq_X_pow_sub_one hn] diff --git a/Mathlib/RingTheory/PowerBasis.lean b/Mathlib/RingTheory/PowerBasis.lean index 78a12b429b1e02..1d58036c3c85fe 100644 --- a/Mathlib/RingTheory/PowerBasis.lean +++ b/Mathlib/RingTheory/PowerBasis.lean @@ -442,11 +442,7 @@ variable [Algebra A S] [Algebra A S'] theorem minpolyGen_map (pb : PowerBasis A S) (e : S ≃ₐ[A] S') : (pb.map e).minpolyGen = pb.minpolyGen := by - dsimp only [minpolyGen, map_dim] - -- Turn `Fin (pb.map e).dim` into `Fin pb.dim` - simp only [LinearEquiv.trans_apply, map_basis, Basis.map_repr, map_gen, - AlgEquiv.toLinearEquiv_apply, e.toLinearEquiv_symm, map_pow, - AlgEquiv.symm_apply_apply, sub_right_inj] + simp @[simp] theorem equivOfRoot_map (pb : PowerBasis A S) (e : S ≃ₐ[A] S') (h₁ h₂) : diff --git a/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean b/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean index 5a56779cfcc018..c3eeda4267370c 100644 --- a/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean +++ b/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean @@ -639,7 +639,7 @@ end IsLocalRing /-- If `f` is a polynomial over `A`, `g` and `h` are power series over `A`, then `PowerSeries.IsWeierstrassFactorizationAt g f h I` is a `Prop` which asserts that `f` is -distingushed at `I`, `h` is a unit, such that `g = f * h`. -/ +distinguished at `I`, `h` is a unit, such that `g = f * h`. -/ @[mk_iff] structure IsWeierstrassFactorizationAt (g : A⟦X⟧) (f : A[X]) (h : A⟦X⟧) (I : Ideal A) : Prop where isDistinguishedAt : f.IsDistinguishedAt I diff --git a/Mathlib/RingTheory/RootsOfUnity/Basic.lean b/Mathlib/RingTheory/RootsOfUnity/Basic.lean index 202ea47694ca50..0080837f2af286 100644 --- a/Mathlib/RingTheory/RootsOfUnity/Basic.lean +++ b/Mathlib/RingTheory/RootsOfUnity/Basic.lean @@ -178,8 +178,6 @@ def rootsOfUnityEquivNthRoots : rootsOfUnity k R ≃ { x // x ∈ nthRoots k (1 rcases x with ⟨x, hx⟩; rw [mem_nthRoots <| NeZero.pos k] at hx simp only [← pow_succ, ← pow_succ', hx, tsub_add_cancel_of_le NeZero.one_le] simp only [mem_rootsOfUnity, Units.ext_iff, Units.val_pow_eq_pow_val, hx, Units.val_one] - left_inv := by rintro ⟨x, hx⟩; ext; rfl - right_inv := by rintro ⟨x, hx⟩; ext; rfl variable {k R} diff --git a/Mathlib/RingTheory/RootsOfUnity/Minpoly.lean b/Mathlib/RingTheory/RootsOfUnity/Minpoly.lean index 5c658f30aa59f2..ec6afbf9cdc5ba 100644 --- a/Mathlib/RingTheory/RootsOfUnity/Minpoly.lean +++ b/Mathlib/RingTheory/RootsOfUnity/Minpoly.lean @@ -60,7 +60,7 @@ theorem minpoly_dvd_x_pow_sub_one : minpoly ℤ μ ∣ X ^ n - 1 := by theorem separable_minpoly_mod {p : ℕ} [Fact p.Prime] (hdiv : ¬p ∣ n) : Separable (map (Int.castRingHom (ZMod p)) (minpoly ℤ μ)) := by have hdvd : map (Int.castRingHom (ZMod p)) (minpoly ℤ μ) ∣ X ^ n - 1 := by - convert RingHom.map_dvd (mapRingHom (Int.castRingHom (ZMod p))) + convert _root_.map_dvd (mapRingHom (Int.castRingHom (ZMod p))) (minpoly_dvd_x_pow_sub_one h) simp only [map_sub, map_pow, coe_mapRingHom, map_X, map_one] refine Separable.of_dvd (separable_X_pow_sub_C 1 ?_ one_ne_zero) hdvd @@ -94,7 +94,7 @@ theorem minpoly_dvd_pow_mod {p : ℕ} [hprime : Fact p.Prime] (hdiv : ¬p ∣ n) map (Int.castRingHom (ZMod p)) Q ^ p = map (Int.castRingHom (ZMod p)) (expand ℤ p Q) := by rw [← ZMod.expand_card, map_expand] rw [hfrob] - apply RingHom.map_dvd (mapRingHom (Int.castRingHom (ZMod p))) + apply _root_.map_dvd (mapRingHom (Int.castRingHom (ZMod p))) exact minpoly_dvd_expand h hdiv /-- Let `P` be the minimal polynomial of a root of unity `μ` and `Q` be the minimal polynomial of @@ -136,7 +136,7 @@ theorem minpoly_eq_pow {p : ℕ} [hprime : Fact p.Prime] (hdiv : ¬p ∣ n) : exact minpoly_dvd_x_pow_sub_one h · apply (map_dvd_map (Int.castRingHom ℚ) Int.cast_injective Qmonic).2 exact minpoly_dvd_x_pow_sub_one (pow_of_prime h hprime.1 hdiv) - replace prod := RingHom.map_dvd (mapRingHom (Int.castRingHom (ZMod p))) prod + replace prod := _root_.map_dvd (mapRingHom (Int.castRingHom (ZMod p))) prod rw [coe_mapRingHom, Polynomial.map_mul, Polynomial.map_sub, Polynomial.map_one, Polynomial.map_pow, map_X] at prod obtain ⟨R, hR⟩ := minpoly_dvd_mod_p h hdiv diff --git a/Mathlib/RingTheory/Spectrum/Maximal/Basic.lean b/Mathlib/RingTheory/Spectrum/Maximal/Basic.lean index dc91ef72f56f72..8d3d1890f3386c 100644 --- a/Mathlib/RingTheory/Spectrum/Maximal/Basic.lean +++ b/Mathlib/RingTheory/Spectrum/Maximal/Basic.lean @@ -23,8 +23,6 @@ namespace MaximalSpectrum def equivSubtype : MaximalSpectrum R ≃ {I : Ideal R // I.IsMaximal} where toFun I := ⟨I.asIdeal, I.2⟩ invFun I := ⟨I, I.2⟩ - left_inv _ := rfl - right_inv _ := rfl theorem range_asIdeal : Set.range MaximalSpectrum.asIdeal = {J : Ideal R | J.IsMaximal} := Set.ext fun J ↦ diff --git a/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean b/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean index f0f42fb2b767cd..73c809d713b271 100644 --- a/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean +++ b/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean @@ -182,8 +182,6 @@ def piLocalizationToMaximalEquiv (h : ∀ I : Ideal R, I.IsPrime → I.IsMaximal PiLocalization R ≃+* MaximalSpectrum.PiLocalization R where __ := piLocalizationToMaximal R invFun := Pi.ringHom fun I ↦ Pi.evalRingHom _ (⟨_, h _ I.2⟩ : MaximalSpectrum R) - left_inv _ := rfl - right_inv _ := rfl theorem piLocalizationToMaximal_bijective (h : ∀ I : Ideal R, I.IsPrime → I.IsMaximal) : Function.Bijective (piLocalizationToMaximal R) := diff --git a/Mathlib/RingTheory/Spectrum/Prime/Defs.lean b/Mathlib/RingTheory/Spectrum/Prime/Defs.lean index 4c5c8ef0e47c0e..f7323775114ae7 100644 --- a/Mathlib/RingTheory/Spectrum/Prime/Defs.lean +++ b/Mathlib/RingTheory/Spectrum/Prime/Defs.lean @@ -62,8 +62,6 @@ variable (R) in def equivSubtype : PrimeSpectrum R ≃o {I : Ideal R // I.IsPrime} where toFun I := ⟨I.asIdeal, I.2⟩ invFun I := ⟨I, I.2⟩ - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' := .rfl end PrimeSpectrum diff --git a/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean b/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean index 98c26c3cc43a74..d829bba2bd2cb7 100644 --- a/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean +++ b/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean @@ -93,8 +93,6 @@ lemma le_iff {I J : TwoSidedIdeal R} : I ≤ J ↔ (I : Set R) ⊆ (J : Set R) : def orderIsoRingCon : TwoSidedIdeal R ≃o RingCon R where toFun := TwoSidedIdeal.ringCon invFun := .mk - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' {I J} := Iff.symm <| le_iff.trans ⟨fun h x y r => by rw [rel_iff] at r ⊢; exact h r, fun h x hx => by rw [SetLike.mem_coe, mem_iff] at hx ⊢; exact h hx⟩ @@ -240,8 +238,6 @@ Two-sided-ideals of `A` and that of `Aᵒᵖ` corresponds bijectively to each ot def opOrderIso : TwoSidedIdeal R ≃o TwoSidedIdeal Rᵐᵒᵖ where toFun := op invFun := unop - left_inv _ := rfl - right_inv _ := rfl map_rel_iff' {I' J'} := by simpa [ringCon_le_iff] using RingCon.opOrderIso.map_rel_iff end NonUnitalNonAssocRing diff --git a/Mathlib/RingTheory/Valuation/Basic.lean b/Mathlib/RingTheory/Valuation/Basic.lean index 1576e7695c9a1d..877c8145f6962b 100644 --- a/Mathlib/RingTheory/Valuation/Basic.lean +++ b/Mathlib/RingTheory/Valuation/Basic.lean @@ -939,8 +939,6 @@ def toAddValuation : Valuation R Γ₀ ≃ AddValuation R (Additive Γ₀)ᵒᵈ .trans (congr { toFun := fun x ↦ .ofAdd <| .toDual <| .toDual <| .ofMul x invFun := fun x ↦ x.toAdd.ofDual.ofDual.toMul - left_inv := fun _x ↦ rfl - right_inv := fun _x ↦ rfl map_mul' := fun _x _y ↦ rfl map_le_map_iff' := .rfl }) (AddValuation.ofValuation (R := R) (Γ₀ := (Additive Γ₀)ᵒᵈ)) @@ -950,8 +948,6 @@ def ofAddValuation : AddValuation R (Additive Γ₀)ᵒᵈ ≃ Valuation R Γ₀ AddValuation.toValuation.trans <| congr <| { toFun := fun x ↦ x.toAdd.ofDual.ofDual.toMul invFun := fun x ↦ .ofAdd <| .toDual <| .toDual <| .ofMul x - left_inv := fun _x ↦ rfl - right_inv := fun _x ↦ rfl map_mul' := fun _x _y ↦ rfl map_le_map_iff' := .rfl } diff --git a/Mathlib/RingTheory/Valuation/ValuationSubring.lean b/Mathlib/RingTheory/Valuation/ValuationSubring.lean index 609d0caefcdd38..e173e97000155a 100644 --- a/Mathlib/RingTheory/Valuation/ValuationSubring.lean +++ b/Mathlib/RingTheory/Valuation/ValuationSubring.lean @@ -432,8 +432,6 @@ def unitGroupMulEquiv : A.unitGroup ≃* Aˣ where -- Porting note: was `Units.inv_mul x` inv_val := Subtype.ext (by simp) } invFun x := ⟨Units.map A.subtype.toMonoidHom x, A.valuation_unit x⟩ - left_inv a := by ext; rfl - right_inv a := by ext; rfl map_mul' a b := by ext; rfl @[simp] diff --git a/Mathlib/SetTheory/Cardinal/Basic.lean b/Mathlib/SetTheory/Cardinal/Basic.lean index be1366c06d0afe..a2ca9f2d44a2c3 100644 --- a/Mathlib/SetTheory/Cardinal/Basic.lean +++ b/Mathlib/SetTheory/Cardinal/Basic.lean @@ -623,12 +623,10 @@ theorem mk_emptyCollection (α : Type u) : #(∅ : Set α) = 0 := mk_eq_zero _ theorem mk_emptyCollection_iff {α : Type u} {s : Set α} : #s = 0 ↔ s = ∅ := by - constructor - · intro h - rw [mk_eq_zero_iff] at h - exact eq_empty_iff_forall_notMem.2 fun x hx => h.elim' ⟨x, hx⟩ - · rintro rfl - exact mk_emptyCollection _ + rw [mk_eq_zero_iff, isEmpty_coe_sort] + +lemma mk_set_ne_zero_iff {α : Type u} (s : Set α) : #s ≠ 0 ↔ s.Nonempty := by + rw [mk_ne_zero_iff, nonempty_coe_sort] @[simp] theorem mk_univ {α : Type u} : #(@univ α) = #α := @@ -826,6 +824,15 @@ theorem mk_diff_add_mk {S T : Set α} (h : T ⊆ S) : #(S \ T : Set α) + #T = # refine (mk_union_of_disjoint <| ?_).symm.trans <| by rw [diff_union_of_subset h] exact disjoint_sdiff_self_left +lemma diff_nonempty_of_mk_lt_mk {S T : Set α} (h : #S < #T) : (T \ S).Nonempty := by + rw [← mk_set_ne_zero_iff] + intro h' + exact h.not_ge ((le_mk_diff_add_mk T S).trans (by simp [h'])) + +lemma compl_nonempty_of_mk_lt_mk {S : Set α} (h : #S < #α) : Sᶜ.Nonempty := by + rw [← mk_univ (α := α)] at h + simpa [Set.compl_eq_univ_diff] using diff_nonempty_of_mk_lt_mk h + theorem mk_union_le_aleph0 {α} {P Q : Set α} : #(P ∪ Q : Set α) ≤ ℵ₀ ↔ #P ≤ ℵ₀ ∧ #Q ≤ ℵ₀ := by simp only [le_aleph0_iff_subtype_countable, mem_union, setOf_mem_eq, Set.union_def, diff --git a/Mathlib/SetTheory/Cardinal/Finite.lean b/Mathlib/SetTheory/Cardinal/Finite.lean index 55b7597ff4d9c3..180f95ffb9fb89 100644 --- a/Mathlib/SetTheory/Cardinal/Finite.lean +++ b/Mathlib/SetTheory/Cardinal/Finite.lean @@ -56,6 +56,10 @@ lemma card_eq_card_toFinset (s : Set α) [Fintype s] : Nat.card s = s.toFinset.c lemma card_eq_card_finite_toFinset {s : Set α} (hs : s.Finite) : Nat.card s = hs.toFinset.card := by simp only [← Nat.card_eq_finsetCard, hs.mem_toFinset] +theorem subtype_card {p : α → Prop} (s : Finset α) (H : ∀ x : α, x ∈ s ↔ p x) : + Nat.card { x // p x } = Finset.card s := by + rw [← Fintype.subtype_card s H, Fintype.card_eq_nat_card] + @[simp] theorem card_of_isEmpty [IsEmpty α] : Nat.card α = 0 := by simp [Nat.card] @[simp] lemma card_eq_zero_of_infinite [Infinite α] : Nat.card α = 0 := mk_toNat_of_infinite diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean index ca5afcf5c9a980..9d96e701366155 100644 --- a/Mathlib/Tactic.lean +++ b/Mathlib/Tactic.lean @@ -101,7 +101,6 @@ import Mathlib.Tactic.FunProp.Differentiable import Mathlib.Tactic.FunProp.Elab import Mathlib.Tactic.FunProp.FunctionData import Mathlib.Tactic.FunProp.Mor -import Mathlib.Tactic.FunProp.StateList import Mathlib.Tactic.FunProp.Theorems import Mathlib.Tactic.FunProp.ToBatteries import Mathlib.Tactic.FunProp.Types @@ -109,6 +108,9 @@ import Mathlib.Tactic.GCongr import Mathlib.Tactic.GCongr.Core import Mathlib.Tactic.GCongr.CoreAttrs import Mathlib.Tactic.GCongr.ForwardAttr +import Mathlib.Tactic.GRewrite +import Mathlib.Tactic.GRewrite.Core +import Mathlib.Tactic.GRewrite.Elab import Mathlib.Tactic.Generalize import Mathlib.Tactic.GeneralizeProofs import Mathlib.Tactic.Group @@ -245,6 +247,7 @@ import Mathlib.Tactic.SetLike import Mathlib.Tactic.SimpIntro import Mathlib.Tactic.SimpRw import Mathlib.Tactic.Simproc.ExistsAndEq +import Mathlib.Tactic.Simproc.Factors import Mathlib.Tactic.Simps.Basic import Mathlib.Tactic.Simps.NotationClass import Mathlib.Tactic.SplitIfs diff --git a/Mathlib/Tactic/CategoryTheory/IsoReassoc.lean b/Mathlib/Tactic/CategoryTheory/IsoReassoc.lean index 969f7d1ec02eab..045b2136fb1d29 100644 --- a/Mathlib/Tactic/CategoryTheory/IsoReassoc.lean +++ b/Mathlib/Tactic/CategoryTheory/IsoReassoc.lean @@ -12,7 +12,7 @@ We extend `reassoc` and `reassoc_of%` for equality of isomorphisms. Adding `@[reassoc]` to a lemma named `F` of shape `∀ .., f = g`, where `f g : X ≅ Y` in some category will create a new lemma named `F_assoc` of shape `∀ .. {Z : C} (h : Y ≅ Z), f ≪≫ h = g ≪≫ h` -but with the conclusions simplified using basic propertions in isomorphisms in a category +but with the conclusions simplified using basic proportions in isomorphisms in a category (`Iso.trans_refl`, `Iso.refl_trans`, `Iso.trans_assoc`, `Iso.trans_symm`, `Iso.symm_self_id` and `Iso.self_symm_id`). diff --git a/Mathlib/Tactic/Common.lean b/Mathlib/Tactic/Common.lean index bcd4a49909bb9c..08408442b0be81 100644 --- a/Mathlib/Tactic/Common.lean +++ b/Mathlib/Tactic/Common.lean @@ -14,6 +14,7 @@ import ImportGraph.Imports -- Import common Batteries tactics and commands import Batteries.Tactic.Basic +import Batteries.Tactic.Case import Batteries.Tactic.HelpCmd -- Import syntax for leansearch @@ -52,9 +53,8 @@ import Mathlib.Tactic.ExistsI import Mathlib.Tactic.ExtractGoal import Mathlib.Tactic.FailIfNoProgress import Mathlib.Tactic.Find --- `gcongr` currently imports `Algebra.Order.Field.Power` and thence `Algebra.CharZero.Lemmas` --- Hopefully this can be rearranged. --- import Mathlib.Tactic.GCongr +import Mathlib.Tactic.GCongr +import Mathlib.Tactic.GRewrite import Mathlib.Tactic.GeneralizeProofs import Mathlib.Tactic.GuardGoalNums import Mathlib.Tactic.GuardHypNums diff --git a/Mathlib/Tactic/ComputeDegree.lean b/Mathlib/Tactic/ComputeDegree.lean index ba2e8c8da198f8..1fdf015ebec1df 100644 --- a/Mathlib/Tactic/ComputeDegree.lean +++ b/Mathlib/Tactic/ComputeDegree.lean @@ -504,3 +504,8 @@ macro "monicity!" : tactic => end Tactic end Mathlib.Tactic.ComputeDegree + +/-! + We register `compute_degree` with the `hint` tactic. + -/ +register_hint compute_degree diff --git a/Mathlib/Tactic/Core.lean b/Mathlib/Tactic/Core.lean index f3b7be3bc22e26..997a21cefd5737 100644 --- a/Mathlib/Tactic/Core.lean +++ b/Mathlib/Tactic/Core.lean @@ -72,11 +72,12 @@ def setProtected {m : Type → Type} [MonadEnv m] (nm : Name) : m Unit := /-- Introduce variables, giving them names from a specified list. -/ def MVarId.introsWithBinderIdents - (g : MVarId) (ids : List (TSyntax ``binderIdent)) : + (g : MVarId) (ids : List (TSyntax ``binderIdent)) (maxIntros? : Option Nat := none) : MetaM (List (TSyntax ``binderIdent) × Array FVarId × MVarId) := do let type ← g.getType let type ← instantiateMVars type let n := getIntrosSize type + let n := match maxIntros? with | none => n | some maxIntros => min n maxIntros if n == 0 then return (ids, #[], g) let mut ids := ids diff --git a/Mathlib/Tactic/DeprecateTo.lean b/Mathlib/Tactic/DeprecateTo.lean index a374440b2c34e3..c09c0d3288112c 100644 --- a/Mathlib/Tactic/DeprecateTo.lean +++ b/Mathlib/Tactic/DeprecateTo.lean @@ -6,6 +6,7 @@ Authors: Damiano Testa import Lean.Meta.Tactic.TryThis import Mathlib.Lean.Expr.Basic import Mathlib.Tactic.Lemma +import Std.Time.Format /-! # `deprecate to` -- a deprecation tool @@ -43,9 +44,11 @@ open Lean Elab Term Command /-- Produce the syntax for the command `@[deprecated (since := "YYYY-MM-DD")] alias n := id`. -/ def mkDeprecationStx (id : TSyntax `ident) (n : Name) (dat : Option String := none) : CommandElabM (TSyntax `command) := do - let dat := ← match dat with - | none => IO.Process.run { cmd := "date", args := #["-I"] } - | some s => return s + let dat := ← + match dat with + | none => do + return s!"{(← Std.Time.ZonedDateTime.now).toPlainDate}" + | some s => return s let nd := mkNode `str #[mkAtom ("\"" ++ dat.trimRight ++ "\"")] `(command| @[deprecated (since := $nd)] alias $(mkIdent n) := $id) diff --git a/Mathlib/Tactic/FieldSimp.lean b/Mathlib/Tactic/FieldSimp.lean index 52bf569939d8d8..9ab93ea3112bad 100644 --- a/Mathlib/Tactic/FieldSimp.lean +++ b/Mathlib/Tactic/FieldSimp.lean @@ -213,3 +213,8 @@ elab_rules : tactic _ ← simpLocation r.ctx {} dis loc end Mathlib.Tactic.FieldSimp + +/-! + We register `field_simp` with the `hint` tactic. + -/ +register_hint field_simp diff --git a/Mathlib/Tactic/Finiteness.lean b/Mathlib/Tactic/Finiteness.lean index 3d53bbf8e064df..6de02f1520eb7f 100644 --- a/Mathlib/Tactic/Finiteness.lean +++ b/Mathlib/Tactic/Finiteness.lean @@ -67,3 +67,8 @@ macro (name := finiteness_nonterminal) "finiteness_nonterminal" c:Aesop.tactic_c (config := { introsTransparency? := some .reducible, terminal := false, enableSimp := false, warnOnNonterminal := false }) (rule_sets := [$(Lean.mkIdent `finiteness):ident, -default, -builtin])) + +/-! + We register `finiteness` with the `hint` tactic. + -/ +register_hint finiteness diff --git a/Mathlib/Tactic/FunProp.lean b/Mathlib/Tactic/FunProp.lean index 156b6084265631..3509aff61e62f6 100644 --- a/Mathlib/Tactic/FunProp.lean +++ b/Mathlib/Tactic/FunProp.lean @@ -9,8 +9,6 @@ import Mathlib.Tactic.FunProp.Decl import Mathlib.Tactic.FunProp.Elab import Mathlib.Tactic.FunProp.FunctionData import Mathlib.Tactic.FunProp.Mor -import Mathlib.Lean.Meta.RefinedDiscrTree -import Mathlib.Tactic.FunProp.StateList import Mathlib.Tactic.FunProp.Theorems import Mathlib.Tactic.FunProp.ToBatteries import Mathlib.Tactic.FunProp.Types diff --git a/Mathlib/Tactic/FunProp/Elab.lean b/Mathlib/Tactic/FunProp/Elab.lean index d8de53cbfee63d..68299addb44465 100644 --- a/Mathlib/Tactic/FunProp/Elab.lean +++ b/Mathlib/Tactic/FunProp/Elab.lean @@ -70,7 +70,11 @@ def funPropTac : Tactic { config := cfg, disch := disch constToUnfold := .ofArray namesToUnfold _} - let (r?, s) ← funProp goalType ctx |>.run {} + let env ← getEnv + let s := { + morTheorems := morTheoremsExt.getState env + transitionTheorems := transitionTheoremsExt.getState env } + let (r?, s) ← funProp goalType ctx |>.run s if let .some r := r? then goal.assign r.proof else diff --git a/Mathlib/Tactic/FunProp/StateList.lean b/Mathlib/Tactic/FunProp/StateList.lean deleted file mode 100644 index f21d1f591f77a3..00000000000000 --- a/Mathlib/Tactic/FunProp/StateList.lean +++ /dev/null @@ -1,176 +0,0 @@ -/- -Copyright (c) 2023 J. W. Gerbscheid. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: J. W. Gerbscheid --/ - -import Mathlib.Init - -/-! -The combined state and list monad transformer. -`StateListT σ α` is equivalent to `StateT σ (ListT α)` but more efficient. - -WARNING: `StateListT σ α m` is only a monad if `m` is a commutative monad. -For example, -``` -def problem : StateListT Unit (StateM (Array Nat)) Unit := do - Alternative.orElse (pure ()) (fun _ => pure ()) - StateListT.lift $ modify (·.push 0) - StateListT.lift $ modify (·.push 1) - -#eval ((problem.run' ()).run #[]).2 -``` -will yield either `#[0,1,0,1]`, or `#[0,0,1,1]`, depending on the order in which the actions -in the do block are combined. - --/ - -/-! StateList -/ - -namespace Mathlib.Meta.FunProp - -universe u v - -/-- `StateList` is a List with a state associated to each element. -This is used instead of `List (α × σ)` as it is more efficient. -/ -inductive StateList (σ α : Type u) where - /-- .nil is the empty list. -/ - | nil : StateList σ α - /-- If `a : α`, `s : σ` and `l : List α`, then `.cons a s l`, is the - list with first element `a` with state `s` and `l` as the rest of the list. -/ - | cons : α → σ → StateList σ α → StateList σ α - -variable {α β σ : Type u} - -namespace StateList - -private def toList : StateList σ α → List (α × σ) - | .cons a s l => (a, s) :: l.toList - | .nil => [] - -private def toList' : StateList σ α → List α - | .cons a _ l => a :: l.toList' - | .nil => [] - -private def map (f : α → β) : StateList σ α → StateList σ β - | .cons a s l => .cons (f a) s (l.map f) - | .nil => .nil - -private def append : (xs ys : StateList σ α) → StateList σ α - | .nil, bs => bs - | .cons a s l, bs => .cons a s (l.append bs) - -instance : Append (StateList σ α) := ⟨StateList.append⟩ - -@[specialize] -private def foldrM {m} [Monad m] : (f : α → σ → β → m β) → (init : β) → StateList σ α → m β - | _, b, .nil => pure b - | f, b, .cons a s l => do - f a s (← l.foldrM f b) - -end StateList - -/-- The combined state and list monad transformer. -/ -def StateListT (σ : Type u) (m : Type u → Type v) (α : Type u) : Type (max u v) := - σ → m (StateList σ α) - -variable {m : Type u → Type v} [Monad m] - -/-- Run `x` on a given state `s`, returning the list of values with corresponding states. -/ -@[always_inline, inline] -def StateListT.run [Functor m] (x : StateListT σ m α) (s : σ) : m (List (α × σ)) := - StateList.toList <$> x s - -/-- Run `x` on a given state `s`, returning the list of values. -/ -@[always_inline, inline] -def StateListT.run' [Functor m] (x : StateListT σ m α) (s : σ) : m (List α) := - StateList.toList' <$> x s - -/-- The combined state and list monad. -/ -abbrev StateListM (σ α : Type u) : Type u := StateListT σ Id α - -namespace StateListT -section - -@[always_inline, inline] -private def pure (a : α) : StateListT σ m α := - fun s => return StateList.nil.cons a s - -/-- Separately handling lists of length 1 is important to avoid a stack overflow. -/ -@[always_inline, inline] -private def bind (x : StateListT σ m α) (f : α → StateListT σ m β) : StateListT σ m β := - fun s => do match ← x s with - | .nil => return .nil - | .cons a s .nil => f a s - | x => x.foldrM (fun a s bs => return (← f a s) ++ bs) .nil - -@[always_inline, inline] -private def map (f : α → β) (x : StateListT σ m α) : StateListT σ m β := - fun s => StateList.map f <$> x s - -@[always_inline] -instance : Monad (StateListT σ m) where - pure := StateListT.pure - bind := StateListT.bind - map := StateListT.map - -@[always_inline, inline] -private def orElse (x : StateListT σ m α) (y : Unit → StateListT σ m α) : StateListT σ m α := - fun s => (· ++ ·) <$> x s <*> y () s - -@[always_inline, inline] -private def failure : StateListT σ m α := - fun _ => return .nil - -instance : Alternative (StateListT σ m) where - failure := StateListT.failure - orElse := StateListT.orElse - -/-- Return the state from `StateListT σ m`. -/ -@[always_inline, inline] -protected def get : StateListT σ m σ := - fun s => return StateList.nil.cons s s - -/-- Set the state in `StateListT σ m`. -/ -@[always_inline, inline] -protected def set : σ → StateListT σ m PUnit := - fun s' _ => return StateList.nil.cons ⟨⟩ s' - -/-- Modify and get the state in `StateListT σ m`. -/ -@[always_inline, inline] -protected def modifyGet (f : σ → α × σ) : StateListT σ m α := - fun s => let a := f s; return StateList.nil.cons a.1 a.2 - -/-- Lift an action from `m α` to `StateListT σ m α`. -/ -@[always_inline, inline] -protected def lift (t : m α) : StateListT σ m α := - fun s => do let a ← t; return StateList.nil.cons a s - -instance : MonadLift m (StateListT σ m) := ⟨StateListT.lift⟩ - -@[always_inline] -instance : MonadFunctor m (StateListT σ m) := ⟨fun f x s => f (x s)⟩ - -@[always_inline] -instance {ε} [MonadExceptOf ε m] : MonadExceptOf ε (StateListT σ m) := { - throw := StateListT.lift ∘ throwThe ε - tryCatch := fun x c s => tryCatchThe ε (x s) (fun e => c e s) -} - -end -end StateListT - - -instance : MonadStateOf σ (StateListT σ m) where - get := StateListT.get - set := StateListT.set - modifyGet := StateListT.modifyGet - - -@[always_inline] -instance StateListT.monadControl : MonadControl m (StateListT σ m) where - stM := StateList σ - liftWith := fun f => do let s ← get; liftM (f (fun x => x s)) - restoreM := fun x _ => x - -end Mathlib.Meta.FunProp diff --git a/Mathlib/Tactic/FunProp/Theorems.lean b/Mathlib/Tactic/FunProp/Theorems.lean index a065c66ac21a93..1602b6c9d35a58 100644 --- a/Mathlib/Tactic/FunProp/Theorems.lean +++ b/Mathlib/Tactic/FunProp/Theorems.lean @@ -6,7 +6,7 @@ Authors: Tomáš Skřivan import Mathlib.Tactic.FunProp.Decl import Mathlib.Tactic.FunProp.Types import Mathlib.Tactic.FunProp.FunctionData -import Mathlib.Lean.Meta.RefinedDiscrTree +import Mathlib.Lean.Meta.RefinedDiscrTree.Initialize import Mathlib.Lean.Meta.RefinedDiscrTree.Lookup /-! @@ -212,28 +212,10 @@ def getTheoremsForFunction (funName : Name) (funPropName : Name) : -------------------------------------------------------------------------------- -/-- General theorem about a function property used for transition and morphism theorems -/ -structure GeneralTheorem where - /-- function property name -/ - funPropName : Name - /-- theorem name -/ - thmName : Name - /-- discrimination tree keys used to index this theorem -/ - keys : List RefinedDiscrTree.DTExpr - /-- priority -/ - priority : Nat := eval_prio default - deriving Inhabited, BEq - /-- Get proof of a theorem. -/ def GeneralTheorem.getProof (thm : GeneralTheorem) : MetaM Expr := do mkConstWithFreshMVarLevels thm.thmName -/-- Structure holding transition or morphism theorems for `fun_prop` tactic. -/ -structure GeneralTheorems where - /-- Discrimination tree indexing theorems. -/ - theorems : RefinedDiscrTree GeneralTheorem := {} - deriving Inhabited - /-- Extendions for transition or morphism theorems -/ abbrev GeneralTheoremsExt := SimpleScopedEnvExtension GeneralTheorem GeneralTheorems @@ -243,7 +225,8 @@ initialize transitionTheoremsExt : GeneralTheoremsExt ← name := by exact decl_name% initial := {} addEntry := fun d e => - {d with theorems := e.keys.foldl (RefinedDiscrTree.insertDTExpr · · e) d.theorems} + {d with theorems := e.keys.foldl (fun thms (key, entry) => + RefinedDiscrTree.insert thms key (entry, e)) d.theorems} } /-- Get transition theorems applicable to `e`. @@ -251,11 +234,11 @@ initialize transitionTheoremsExt : GeneralTheoremsExt ← For example calling on `e` equal to `Continuous f` might return theorems implying continuity from linearity over finite dimensional spaces or differentiability. -/ def getTransitionTheorems (e : Expr) : FunPropM (Array GeneralTheorem) := do - let ext := transitionTheoremsExt.getState (← getEnv) - let candidates ← withConfig (fun cfg => { cfg with iota := false, zeta := false }) <| - ext.theorems.getMatchWithScore e false - let candidates := candidates.map (·.1) |>.flatten - return candidates + let thms := (← get).transitionTheorems.theorems + let (candidates, thms) ← withConfig (fun cfg => { cfg with iota := false, zeta := false }) <| + thms.getMatch e false true + modify ({ · with transitionTheorems := ⟨thms⟩ }) + return candidates.toArray /-- Environment extension for morphism theorems. -/ initialize morTheoremsExt : GeneralTheoremsExt ← @@ -263,7 +246,8 @@ initialize morTheoremsExt : GeneralTheoremsExt ← name := by exact decl_name% initial := {} addEntry := fun d e => - {d with theorems := e.keys.foldl (RefinedDiscrTree.insertDTExpr · · e) d.theorems} + {d with theorems := e.keys.foldl (fun thms (key, entry) => + RefinedDiscrTree.insert thms key (entry, e)) d.theorems} } @@ -272,11 +256,11 @@ initialize morTheoremsExt : GeneralTheoremsExt ← For example calling on `e` equal to `Continuous f` for `f : X→L[ℝ] Y` would return theorem inferring continuity from the bundled morphism. -/ def getMorphismTheorems (e : Expr) : FunPropM (Array GeneralTheorem) := do - let ext := morTheoremsExt.getState (← getEnv) - let candidates ← withConfig (fun cfg => { cfg with iota := false, zeta := false }) <| - ext.theorems.getMatchWithScore e false - let candidates := candidates.map (·.1) |>.flatten - return candidates + let thms := (← get).morTheorems.theorems + let (candidates, thms) ← withConfig (fun cfg => { cfg with iota := false, zeta := false }) <| + thms.getMatch e false true + modify ({ · with morTheorems := ⟨thms⟩ }) + return candidates.toArray -------------------------------------------------------------------------------- @@ -360,7 +344,7 @@ def getTheoremFromConst (declName : Name) (prio : Nat := eval_prio default) : Me } | .fvar .. => let (_,_,b') ← forallMetaTelescope info.type - let keys := ← RefinedDiscrTree.mkDTExprs b' false + let keys ← RefinedDiscrTree.initializeLazyEntryWithEta b' let thm : GeneralTheorem := { funPropName := funPropName thmName := declName diff --git a/Mathlib/Tactic/FunProp/Types.lean b/Mathlib/Tactic/FunProp/Types.lean index 75d41df51f36ed..ebc812a0edde0f 100644 --- a/Mathlib/Tactic/FunProp/Types.lean +++ b/Mathlib/Tactic/FunProp/Types.lean @@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Tomáš Skřivan -/ import Mathlib.Tactic.FunProp.FunctionData +import Mathlib.Lean.Meta.RefinedDiscrTree.Basic /-! ## `funProp` @@ -94,6 +95,24 @@ structure Context where /-- current transition depth -/ transitionDepth := 0 +/-- General theorem about a function property used for transition and morphism theorems -/ +structure GeneralTheorem where + /-- function property name -/ + funPropName : Name + /-- theorem name -/ + thmName : Name + /-- discrimination tree keys used to index this theorem -/ + keys : List (RefinedDiscrTree.Key × RefinedDiscrTree.LazyEntry) + /-- priority -/ + priority : Nat := eval_prio default + deriving Inhabited + +/-- Structure holding transition or morphism theorems for `fun_prop` tactic. -/ +structure GeneralTheorems where + /-- Discrimination tree indexing theorems. -/ + theorems : RefinedDiscrTree GeneralTheorem := {} + deriving Inhabited + /-- `fun_prop` state -/ structure State where /-- Simp's cache is used as the `fun_prop` tactic is designed to be used inside of simp and @@ -105,6 +124,10 @@ structure State where numSteps := 0 /-- Log progress and failures messages that should be displayed to the user at the end. -/ msgLog : List String := [] + /-- `RefinedDiscrTree` is lazy, so we store the partially evaluated tree. -/ + morTheorems : GeneralTheorems + /-- `RefinedDiscrTree` is lazy, so we store the partially evaluated tree. -/ + transitionTheorems : GeneralTheorems /-- Increase depth -/ def Context.increaseTransitionDepth (ctx : Context) : Context := diff --git a/Mathlib/Tactic/GCongr.lean b/Mathlib/Tactic/GCongr.lean index a6d16260c69c19..4615c6ac1c0069 100644 --- a/Mathlib/Tactic/GCongr.lean +++ b/Mathlib/Tactic/GCongr.lean @@ -3,16 +3,13 @@ Copyright (c) 2023 Mario Carneiro, Heather Macbeth. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Mario Carneiro, Heather Macbeth -/ -import Mathlib.Tactic.Positivity.Core import Mathlib.Tactic.GCongr.CoreAttrs +import Mathlib.Tactic.Hint /-! # Setup for the `gcongr` tactic The core implementation of the `gcongr` ("generalized congruence") tactic is in the file -`Tactic.GCongr.Core`. In this file we set it up for use across the library by listing -`positivity` as a first-pass discharger for side goals (`gcongr_discharger`). -/ - -macro_rules | `(tactic| gcongr_discharger) => `(tactic| positivity) +`Tactic.GCongr.Core`. -/ /-! We register `gcongr` with the `hint` tactic. diff --git a/Mathlib/Tactic/GCongr/Core.lean b/Mathlib/Tactic/GCongr/Core.lean index 52e05c6274c77d..23fa330642446e 100644 --- a/Mathlib/Tactic/GCongr/Core.lean +++ b/Mathlib/Tactic/GCongr/Core.lean @@ -1,14 +1,14 @@ /- Copyright (c) 2023 Mario Carneiro, Heather Macbeth. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Mario Carneiro, Heather Macbeth +Authors: Mario Carneiro, Heather Macbeth, Jovan Gerbscheid -/ import Lean import Batteries.Lean.Except import Batteries.Tactic.Exact -import Mathlib.Tactic.Core +import Mathlib.Lean.Elab.Term import Mathlib.Tactic.GCongr.ForwardAttr -import Mathlib.Order.Defs.PartialOrder +import Mathlib.Order.Defs.Unbundled /-! # The `gcongr` ("generalized congruence") tactic @@ -126,50 +126,70 @@ The `rel` tactic is finishing-only: if fails if any main or side goals are not r namespace Mathlib.Tactic.GCongr open Lean Meta +/-- `GCongrKey` is the key used to store and look up `gcongr` lemmas. -/ +structure GCongrKey where + /-- The name of the relation. For example, `a + b ≤ a + c` has ``relName := `LE.le``. -/ + relName : Name + /-- The name of the head function. For example, `a + b ≤ a + c` has ``head := `HAdd.hAdd``. -/ + head : Name + /-- The array of which arguments in the application of `head` are different. + For example, `a + b ≤ a + c` has `#[false, false, false, false, false, true]`. -/ + varyingArgs : Array Bool +deriving Inhabited, BEq, Hashable + /-- Structure recording the data for a "generalized congruence" (`gcongr`) lemma. -/ structure GCongrLemma where + /-- The name of the lemma. -/ declName : Name - mainSubgoals : Array (Nat × Nat) - varyingArgs : Array Bool + /-- `mainSubgoals` are the subgoals on which `gcongr` will be recursively called. They store + - the index of the hypothesis + - the index of the arguments in the conclusion + - the number of parameters in the hypothesis -/ + mainSubgoals : Array (Nat × Nat × Nat) deriving Inhabited, Repr /-- Environment extension for "generalized congruence" (`gcongr`) lemmas. -/ -initialize gcongrExt : SimpleScopedEnvExtension ((Name × Name × Array Bool) × GCongrLemma) - (Std.HashMap (Name × Name × Array Bool) (Array GCongrLemma)) ← +initialize gcongrExt : SimpleScopedEnvExtension (GCongrKey × GCongrLemma) + (Std.HashMap GCongrKey (Array GCongrLemma)) ← registerSimpleScopedEnvExtension { addEntry := fun m (n, lem) => m.insert n ((m.getD n #[]).push lem) initial := {} } -/-- Attribute marking "generalized congruence" (`gcongr`) lemmas. Such lemmas must have a -conclusion of a form such as `f x₁ y z₁ ∼ f x₂ y z₂`; that is, a relation between the application of -a function to two argument lists, in which the "varying argument" pairs (here `x₁`/`x₂` and -`z₁`/`z₂`) are all free variables. - -The antecedents of such a lemma are classified as generating "main goals" if they are of the form -`x₁ ≈ x₂` for some "varying argument" pair `x₁`/`x₂` (and a possibly different relation `≈` to `∼`), -or more generally of the form `∀ i h h' j h'', f₁ i j ≈ f₂ i j` (say) for some "varying argument" -pair `f₁`/`f₂`. (Other antecedents are considered to generate "side goals".) The index of the -"varying argument" pair corresponding to each "main" antecedent is recorded. - -Lemmas involving `<` or `≤` can also be marked `@[bound]` for use in the related `bound` tactic. -/ -initialize registerBuiltinAttribute { - name := `gcongr - descr := "generalized congruence" - add := fun decl _ kind ↦ MetaM.run' do - let declTy := (← getConstInfo decl).type - withReducible <| forallTelescopeReducing declTy fun xs targetTy => do - let fail (m : MessageData) := throwError "\ +/-- Given an application `f a₁ .. aₙ`, return the name of `f`, and the array of arguments `aᵢ`. -/ +def getCongrAppFnArgs (e : Expr) : Option (Name × Array Expr) := + match e.cleanupAnnotations with + | .forallE n d b bi => + -- We determine here whether an arrow is an implication or a forall + -- this approach only works if LHS and RHS are both dependent or both non-dependent + if b.hasLooseBVars then + some (`_Forall, #[.lam n d b bi]) + else + some (`_Implies, #[d, b]) + | e => e.withApp fun f args => f.constName?.map (·, args) + +/-- If `e` is of the form `r a b`, return `(r, a, b)`. -/ +def getRel (e : Expr) : Option (Name × Expr × Expr) := + match e with + | .app (.app rel lhs) rhs => rel.getAppFn.constName?.map (·, lhs, rhs) + | .forallE _ lhs rhs _ => + if !rhs.hasLooseBVars then + some (`_Implies, lhs, rhs) + else + none + | _ => none + +/-- Construct the `GCongrKey` and `GCongrLemma` data from a given lemma. -/ +def makeGCongrLemma (decl : Name) (declTy : Expr) (numHyps : Nat) : + MetaM (GCongrKey × GCongrLemma) := do + withReducible <| forallBoundedTelescope declTy numHyps fun xs targetTy => do + let fail {α} (m : MessageData) : MetaM α := throwError "\ @[gcongr] attribute only applies to lemmas proving f x₁ ... xₙ ∼ f x₁' ... xₙ'.\n \ {m} in the conclusion of {declTy}" -- verify that conclusion of the lemma is of the form `f x₁ ... xₙ ∼ f x₁' ... xₙ'` - let .app (.app rel lhs) rhs ← whnf targetTy | - fail "No relation with at least two arguments found" - let some relName := rel.getAppFn.constName? | fail "No relation found" - let (some head, lhsArgs) := lhs.withApp fun e a => (e.constName?, a) | - fail "LHS is not a function" - let (some head', rhsArgs) := rhs.withApp fun e a => (e.constName?, a) | - fail "RHS is not a function" + let some (relName, lhs, rhs) := getRel (← whnf targetTy) | fail "No relation found" + let some (head, lhsArgs) := getCongrAppFnArgs lhs | fail "LHS is not suitable for congruence" + let some (head', rhsArgs) := getCongrAppFnArgs rhs | fail "RHS is not suitable for congruence" unless head == head' && lhsArgs.size == rhsArgs.size do fail "LHS and RHS do not have the same head function and arity" let mut varyingArgs := #[] @@ -179,42 +199,89 @@ initialize registerBuiltinAttribute { for e1 in lhsArgs, e2 in rhsArgs do -- we call such a pair a "varying argument" pair if the LHS/RHS inputs are not defeq -- (and not proofs) - let isEq := (← isDefEq e1 e2) || ((← isProof e1) && (← isProof e2)) + let isEq ← isDefEq e1 e2 <||> (isProof e1 <&&> isProof e2) if !isEq then - let e1 := e1.eta - let e2 := e2.eta -- verify that the "varying argument" pairs are free variables (after eta-reduction) - unless e1.isFVar && e2.isFVar do fail "Not all arguments are free variables" + let .fvar e1 := e1.eta | fail "Not all arguments are free variables" + let .fvar e2 := e2.eta | fail "Not all arguments are free variables" -- add such a pair to the `pairs` array pairs := pairs.push (varyingArgs.size, e1, e2) -- record in the `varyingArgs` array a boolean (true for varying, false if LHS/RHS are defeq) varyingArgs := varyingArgs.push !isEq + if varyingArgs.all not then + fail "LHS and RHS are the same" let mut mainSubgoals := #[] let mut i := 0 -- iterate over antecedents `hyp` to the lemma for hyp in xs do - mainSubgoals ← forallTelescopeReducing (← inferType hyp) fun _args hypTy => do - let mut mainSubgoals := mainSubgoals + mainSubgoals ← forallTelescopeReducing (← inferType hyp) fun args hypTy => do -- pull out the conclusion `hypTy` of the antecedent, and check whether it is of the form -- `lhs₁ _ ... _ ≈ rhs₁ _ ... _` (for a possibly different relation `≈` than the relation -- `rel` above) - if let .app (.app _ lhs₁) rhs₁ ← whnf hypTy then - let lhs₁ := lhs₁.getAppFn - let rhs₁ := rhs₁.getAppFn + let hypTy ← whnf hypTy + if let some (_, lhs₁, rhs₁) := getRel hypTy then + if let .fvar lhs₁ := lhs₁.getAppFn then + if let .fvar rhs₁ := rhs₁.getAppFn then -- check whether `(lhs₁, rhs₁)` is in some order one of the "varying argument" pairs from -- the conclusion to the lemma - if let some j ← pairs.findM? fun (_, e1, e2) => - isDefEq lhs₁ e1 <&&> isDefEq rhs₁ e2 <||> - isDefEq lhs₁ e2 <&&> isDefEq rhs₁ e1 + if let some j := pairs.find? fun (_, e1, e2) => + lhs₁ == e1 && rhs₁ == e2 || lhs₁ == e2 && rhs₁ == e1 then -- if yes, record the index of this antecedent as a "main subgoal", together with the -- index of the "varying argument" pair it corresponds to - mainSubgoals := mainSubgoals.push (i, j.1) - pure mainSubgoals + return mainSubgoals.push (i, j.1, args.size) + else + -- now check whether `hypTy` is of the form `rhs₁ _ ... _`, + -- and whether the last hypothesis is of the form `lhs₁ _ ... _`. + if let .fvar rhs₁ := hypTy.getAppFn then + if let some lastFVar := args.back? then + if let .fvar lhs₁ := (← inferType lastFVar).getAppFn then + if let some j := pairs.find? fun (_, e1, e2) => + lhs₁ == e1 && rhs₁ == e2 || lhs₁ == e2 && rhs₁ == e1 + then + return mainSubgoals.push (i, j.1, args.size - 1) + return mainSubgoals i := i + 1 -- store all the information from this parse of the lemma's structure in a `GCongrLemma` - gcongrExt.add - ((relName, head, varyingArgs), { declName := decl, mainSubgoals, varyingArgs }) kind + return ({ relName, head, varyingArgs }, { declName := decl, mainSubgoals }) + + +/-- Attribute marking "generalized congruence" (`gcongr`) lemmas. Such lemmas must have a +conclusion of a form such as `f x₁ y z₁ ∼ f x₂ y z₂`; that is, a relation between the application of +a function to two argument lists, in which the "varying argument" pairs (here `x₁`/`x₂` and +`z₁`/`z₂`) are all free variables. + +The antecedents of such a lemma are classified as generating "main goals" if they are of the form +`x₁ ≈ x₂` for some "varying argument" pair `x₁`/`x₂` (and a possibly different relation `≈` to `∼`), +or more generally of the form `∀ i h h' j h'', f₁ i j ≈ f₂ i j` (say) for some "varying argument" +pair `f₁`/`f₂`. (Other antecedents are considered to generate "side goals".) The index of the +"varying argument" pair corresponding to each "main" antecedent is recorded. + +Lemmas involving `<` or `≤` can also be marked `@[bound]` for use in the related `bound` tactic. -/ +initialize registerBuiltinAttribute { + name := `gcongr + descr := "generalized congruence" + add := fun decl _ kind ↦ MetaM.run' do + let declTy := (← getConstInfo decl).type + let arity := declTy.getForallArity + -- We have to determine how many of the hypotheses should be introduced for + -- processing the `gcongr` lemma. This is because of implication lemmas like `Or.imp`, + -- which we treat as having conclusion `a ∨ b → c ∨ d` instead of just `c ∨ d`. + -- Since there is only one possible arity at which the `gcongr` lemma will be accepted, + -- we simply attempt to process the lemmas at the different possible arities. + try + gcongrExt.add (← makeGCongrLemma decl declTy arity) kind + catch e => try + guard (1 ≤ arity) + gcongrExt.add (← makeGCongrLemma decl declTy (arity - 1)) kind + catch _ => try + -- We need to use `arity - 2` for lemmas such as `imp_imp_imp` and `forall_imp`. + guard (2 ≤ arity) + gcongrExt.add (← makeGCongrLemma decl declTy (arity - 2)) kind + catch _ => + -- If none of the arities work, we throw the error of the first attempt. + throw e + } initialize registerTraceClass `Meta.gcongr @@ -240,10 +307,6 @@ open Elab Tactic goal.assignIfDefEq (← mkAppOptM ``Eq.subst #[h, m]) goal.applyRfl -/-- See if the term is `a < b` and the goal is `a ≤ b`. -/ -@[gcongr_forward] def exactLeOfLt : ForwardExt where - eval h goal := do goal.assignIfDefEq (← mkAppM ``le_of_lt #[h]) - /-- See if the term is `a ∼ b` with `∼` symmetric and the goal is `b ∼ a`. -/ @[gcongr_forward] def symmExact : ForwardExt where eval h goal := do (← goal.applySymm).assignIfDefEq h @@ -283,6 +346,57 @@ def gcongrForwardDischarger (goal : MVarId) : MetaM Unit := Elab.Term.TermElabM. -- run `Lean.MVarId.gcongrForward` on each one goal.gcongrForward hs +/-- Determine whether `template` contains a `?_`. +This guides the `gcongr` tactic when it is given a template. -/ +def containsHole (template : Expr) : MetaM Bool := do + let mctx ← getMCtx + let hasMVar := template.findMVar? fun mvarId => + if let some mdecl := mctx.findDecl? mvarId then + mdecl.kind matches .syntheticOpaque + else + false + return hasMVar.isSome + +section Trans + +/-! +The lemmas `rel_imp_rel`, `rel_trans` and `rel_trans'` are too general to be tagged with +`@[gcongr]`, so instead we use `getTransLemma?` to look up these lemmas. +-/ + +variable {α : Sort*} {r : α → α → Prop} [IsTrans α r] {a b c d : α} + +lemma rel_imp_rel (h₁ : r c a) (h₂ : r b d) : r a b → r c d := + fun h => IsTrans.trans c b d (IsTrans.trans c a b h₁ h) h₂ + +lemma rel_trans (h : r a b) : r b c → r a c := IsTrans.trans a b c h +lemma rel_trans' (h : r b c) : r a b → r a c := fun h' => rel_trans h' h + +/-- +`getTransLemma?` constructs a `GCongrLemma` for `gcongr` goals of the form `a ≺ b → c ≺ d`. +This will be tried if there is no other available `@[gcongr]` lemma. +For example, the relation `a ≡ b [ZMOD n]` has an instance of `IsTrans`, so a congruence of the form +`a ≡ b [ZMOD n] → c ≡ d [ZMOD n]` can be solved with `rel_imp_rel`, `rel_trans` or `rel_trans'`. +-/ +def getTransLemma? (key : GCongrKey) : Array GCongrLemma := Id.run do + -- check that the relation is an implication + if key.relName != `_Implies then #[] else + let num := key.varyingArgs.size + if h : 2 ≤ num then + if key.varyingArgs.any id (stop := num - 2) then #[] else + match key.varyingArgs[num - 2], key.varyingArgs[num - 1] with + | true, true => + #[{ declName := ``rel_imp_rel, mainSubgoals := #[(7, num - 2, 0), (8, num - 1, 0)] }] + | true, false => + #[{ declName := ``rel_trans, mainSubgoals := #[(6, num - 2, 0)] }] + | false, true => + #[{ declName := ``rel_trans', mainSubgoals := #[(6, num - 1, 0)] }] + | _, _ => #[] + else + #[] + +end Trans + /-- The core of the `gcongr` tactic. Parse a goal into the form `(f _ ... _) ∼ (f _ ... _)`, look up any relevant @[gcongr] lemmas, try to apply them, recursively run the tactic itself on "main" goals which are generated, and run the discharger on side goals which are generated. If there @@ -290,6 +404,7 @@ is a user-provided template, first check that the template asks us to descend th match. -/ partial def _root_.Lean.MVarId.gcongr (g : MVarId) (template : Option Expr) (names : List (TSyntax ``binderIdent)) + (inGRewrite : Bool := false) (mainGoalDischarger : MVarId → MetaM Unit := gcongrForwardDischarger) (sideGoalDischarger : MVarId → MetaM Unit := gcongrDischarger) : MetaM (Bool × List (TSyntax ``binderIdent) × Array MVarId) := g.withContext do @@ -308,24 +423,23 @@ partial def _root_.Lean.MVarId.gcongr if let .mvar mvarId := tpl.getAppFn then if let .syntheticOpaque ← mvarId.getKind then try mainGoalDischarger g; return (true, names, #[]) - catch _ => return (false, names, #[g]) + catch ex => + if inGRewrite then throw ex else return (false, names, #[g]) -- (ii) if the template is *not* `?_` then continue on. -- Check that the goal is of the form `rel (lhsHead _ ... _) (rhsHead _ ... _)` - let .app (.app rel lhs) rhs ← withReducible g.getType' - | throwError "gcongr failed, not a relation" - let some relName := rel.getAppFn.constName? - | throwError "gcongr failed, relation head {rel} is not a constant" - let (some lhsHead, lhsArgs) := lhs.withApp fun e a => (e.constName?, a) + let rel ← withReducible g.getType' + let some (relName, lhs, rhs) := getRel rel | throwError "gcongr failed, {rel} is not a relation" + let some (lhsHead, lhsArgs) := getCongrAppFnArgs lhs | if template.isNone then return (false, names, #[g]) - throwError "gcongr failed, {lhs} is not a constant" - let (some rhsHead, rhsArgs) := rhs.withApp fun e a => (e.constName?, a) + throwError "gcongr failed, the head of {lhs} is not a constant" + let some (rhsHead, rhsArgs) := getCongrAppFnArgs rhs | if template.isNone then return (false, names, #[g]) - throwError "gcongr failed, {rhs} is not a constant" + throwError "gcongr failed, the head of {rhs} is not a constant" -- B. If there is a template, check that it is of the form `tplHead _ ... _` and that -- `tplHead = lhsHead = rhsHead` let tplArgs ← if let some tpl := template then - let (some tplHead, tplArgs) := tpl.withApp fun e a => (e.constName?, a) - | throwError "gcongr failed, {tpl} is not a constant" + let some (tplHead, tplArgs) := getCongrAppFnArgs tpl + | throwError "gcongr failed, the head of {tpl} is not a constant" unless tplHead == lhsHead && tplArgs.size == rhsArgs.size do throwError "expected {tplHead}, got {lhsHead}\n{lhs}" unless tplHead == rhsHead && tplArgs.size == rhsArgs.size do @@ -333,14 +447,7 @@ partial def _root_.Lean.MVarId.gcongr -- and also build an array of `Expr` corresponding to the arguments `_ ... _` to `tplHead` in -- the template (these will be used in recursive calls later), and an array of booleans -- according to which of these contain `?_` - tplArgs.mapM fun tpl => do - let mctx ← getMCtx - let hasMVar := tpl.findMVar? fun mvarId => - if let some mdecl := mctx.findDecl? mvarId then - mdecl.kind matches .syntheticOpaque - else - false - pure (some tpl, hasMVar.isSome) + tplArgs.mapM fun tpl => return (some tpl, ← containsHole tpl) -- A. If there is no template, check that `lhs = rhs` else unless lhsHead == rhsHead && lhsArgs.size == rhsArgs.size do @@ -364,7 +471,8 @@ partial def _root_.Lean.MVarId.gcongr -- Look up the `@[gcongr]` lemmas whose conclusion has the same relation and head function as -- the goal and whether the boolean-array of varying/nonvarying arguments of such -- a lemma matches `varyingArgs`. - for lem in (gcongrExt.getState (← getEnv)).getD (relName, lhsHead, varyingArgs) #[] do + let key := { relName, head := lhsHead, varyingArgs } + for lem in (gcongrExt.getState (← getEnv)).getD key #[] ++ getTransLemma? key do let gs ← try -- Try `apply`-ing such a lemma to the goal. Except.ok <$> withReducibleAndInstances (g.apply (← mkConstWithFreshMVarLevels lem.declName)) @@ -387,12 +495,12 @@ partial def _root_.Lean.MVarId.gcongr -- be the same as the head function of the LHS and RHS of our goal), such that the `i`-th -- antecedent to the lemma is a relation between the LHS and RHS `j`-th inputs to the head -- function in the goal. - for (i, j) in lem.mainSubgoals do + for (i, j, numHyps) in lem.mainSubgoals do -- We anticipate that such a "main" subgoal should not have been solved by the `apply` by -- unification ... let some (.mvar mvarId) := args[i]? | panic! "what kind of lemma is this?" -- Introduce all variables and hypotheses in this subgoal. - let (names2, _vs, mvarId) ← mvarId.introsWithBinderIdents names + let (names2, _vs, mvarId) ← mvarId.introsWithBinderIdents names (maxIntros? := numHyps) -- B. If there is a template, look up the part of the template corresponding to the `j`-th -- input to the head function let tpl ← tplArgs[j]!.1.mapM fun e => do @@ -400,7 +508,8 @@ partial def _root_.Lean.MVarId.gcongr pure e -- Recurse: call ourself (`Lean.MVarId.gcongr`) on the subgoal with (if available) the -- appropriate template - let (_, names2, subgoals2) ← mvarId.gcongr tpl names2 mainGoalDischarger sideGoalDischarger + let (_, names2, subgoals2) ← mvarId.gcongr tpl names2 inGRewrite mainGoalDischarger + sideGoalDischarger (names, subgoals) := (names2, subgoals ++ subgoals2) let mut out := #[] -- Also try the discharger on any "side" (i.e., non-"main") goals which were not resolved @@ -424,7 +533,7 @@ partial def _root_.Lean.MVarId.gcongr -- B. If there is a template, and there was no `@[gcongr]` lemma which matched the template, -- fail. | throwError "gcongr failed, no @[gcongr] lemma applies for the template portion \ - {template} and the relation {rel}" + {template} and the relation {relName}" -- B. If there is a template, and there was a `@[gcongr]` lemma which matched the template, but -- it was not possible to `apply` that lemma, then report the error message from `apply`-ing that -- lemma. @@ -468,14 +577,17 @@ side goal `0 ≤ x ^ 2` in the above application of `mul_le_mul_of_nonneg_left`) `gcongr_discharger`, which wraps `positivity` but can also be extended. Side goals not discharged in this way are left for the user. -/ elab "gcongr" template:(colGt term)? - withArg:((" with " (colGt binderIdent)+)?) : tactic => do + withArg:((" with" (ppSpace colGt binderIdent)+)?) : tactic => do let g ← getMainGoal g.withContext do - let .app (.app _rel lhs) _rhs ← withReducible g.getType' + let some (_rel, lhs, _rhs) := getRel (← withReducible g.getType') | throwError "gcongr failed, not a relation" -- Elaborate the template (e.g. `x * ?_ + _`), if the user gave one let template ← template.mapM fun e => do - Term.elabTerm e (← inferType lhs) + let template ← Term.elabPattern e (← inferType lhs) + unless ← containsHole template do + throwError "invalid template {template}, it doesn't contain any `?_`" + pure template -- Get the names from the `with x y z` list let names := (withArg.raw[1].getArgs.map TSyntax.mk).toList -- Time to actually run the core tactic `Lean.MVarId.gcongr`! @@ -514,7 +626,7 @@ elab_rules : tactic let g ← getMainGoal g.withContext do let hyps ← hyps.getElems.mapM (elabTerm · none) - let .app (.app _rel lhs) rhs ← withReducible g.getType' + let some (_rel, lhs, rhs) := getRel (← withReducible g.getType') | throwError "rel failed, goal not a relation" unless ← isDefEq (← inferType lhs) (← inferType rhs) do throwError "rel failed, goal not a relation" @@ -529,7 +641,7 @@ elab_rules : tactic | [] => pure () -- if not, fail and report the unsolved goals | unsolvedGoalStates => do - let unsolvedGoals ← @List.mapM MetaM _ _ _ MVarId.getType unsolvedGoalStates + let unsolvedGoals ← liftMetaM <| List.mapM MVarId.getType unsolvedGoalStates let g := Lean.MessageData.joinSep (unsolvedGoals.map Lean.MessageData.ofExpr) Format.line throwError "rel failed, cannot prove goal by 'substituting' the listed relationships. \ The steps which could not be automatically justified were:\n{g}" diff --git a/Mathlib/Tactic/GCongr/CoreAttrs.lean b/Mathlib/Tactic/GCongr/CoreAttrs.lean index a34b544fb3f7c1..22d02d8d1f2d71 100644 --- a/Mathlib/Tactic/GCongr/CoreAttrs.lean +++ b/Mathlib/Tactic/GCongr/CoreAttrs.lean @@ -12,6 +12,21 @@ In this file we add `gcongr` attribute to lemmas in `Lean.Init`. We may add lemmas from other files imported by `Mathlib/Tactic/GCongr/Core` later. -/ -attribute [gcongr] List.Sublist.append List.Sublist.append_left List.Sublist.append_right - List.Sublist.reverse List.drop_sublist_drop_left List.Sublist.drop Nat.succ_le_succ +variable {a b c : Prop} + +lemma GCongr.imp_trans (h : a → b) : (b → c) → a → c := fun g ha => g (h ha) + +lemma GCongr.imp_right_mono (h : a → b → c) : (a → b) → a → c := + fun h' ha => h ha (h' ha) + +lemma GCongr.and_right_mono (h : a → b → c) : (a ∧ b) → a ∧ c := + fun ⟨ha, hb⟩ => ⟨ha, h ha hb⟩ + +attribute [gcongr] mt + Or.imp Or.imp_left Or.imp_right + And.imp And.imp_left GCongr.and_right_mono + imp_imp_imp GCongr.imp_trans GCongr.imp_right_mono + forall_imp Exists.imp + List.Sublist.append List.Sublist.append_left List.Sublist.append_right + List.Sublist.reverse List.drop_sublist_drop_left List.Sublist.drop List.Perm.append_left List.Perm.append_right List.Perm.append List.Perm.map diff --git a/Mathlib/Tactic/GRewrite.lean b/Mathlib/Tactic/GRewrite.lean new file mode 100644 index 00000000000000..905e5f13f4acab --- /dev/null +++ b/Mathlib/Tactic/GRewrite.lean @@ -0,0 +1,15 @@ +/- +Copyright (c) 2023 Sebastian Zimmer. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sebastian Zimmer, Mario Carneiro, Heather Macbeth, Jovan Gerbscheid +-/ +import Mathlib.Tactic.GRewrite.Elab + +/-! + +# The generalized rewriting tactic + +The `grw`/`grewrite` tactic is a generalization of the `rewrite` tactic that works with relations +other than equality. The core implementation of `grewrite` is in the file `Tactic.GRewrite.Core` + +-/ diff --git a/Mathlib/Tactic/GRewrite/Core.lean b/Mathlib/Tactic/GRewrite/Core.lean new file mode 100644 index 00000000000000..4d0e6ab3915999 --- /dev/null +++ b/Mathlib/Tactic/GRewrite/Core.lean @@ -0,0 +1,134 @@ +/- +Copyright (c) 2023 Sebastian Zimmer. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sebastian Zimmer, Mario Carneiro, Heather Macbeth, Jovan Gerbscheid +-/ +import Mathlib.Tactic.GCongr.Core + +/-! + +# The generalized rewriting tactic + +This module defines the core of the `grw`/`grewrite` tactic. + +TODO: + +The algorithm used to implement `grw` uses the same method as `rw` to determine where to rewrite. +This means that we can get ill-typed results. Moreover, it doesn't detect which occurrences +can be rewritten by `gcongr` and which can't. It also means we cannot rewrite bound variables. + +A better algorithm would be similar to `simp only`, where we recursively enter the subexpression +using `gcongr` lemmas. This is tricky due to the many different `gcongr` for each pattern. + +With the current implementation, we can instead use `nth_grw`. + +-/ + +open Lean Meta + +namespace Mathlib.Tactic + +/-- Given a proof of `a ~ b`, close a goal of the form `a ~' b` or `b ~' a` +for some possibly different relation `~'`. -/ +def GRewrite.dischargeMain (hrel : Expr) (goal : MVarId) : MetaM Unit := do + try + goal.gcongrForward #[hrel] + catch _ => + throwTacticEx `grewrite goal m!"could not discharge {← goal.getType} using {← inferType hrel}" + +/-- The result returned by `Lean.MVarId.grewrite`. -/ +structure GRewriteResult where + /-- The rewritten expression -/ + eNew : Expr + /-- The proof of the implication. The direction depends on the argument `forwardImp`. -/ + impProof : Expr + /-- The new side goals -/ + mvarIds : List MVarId -- new goals + +/-- Configures the behavior of the `rewrite` and `rw` tactics. -/ +structure GRewrite.Config extends Rewrite.Config where + /-- When `useRewrite = true`, switch to using the default `rewrite` tactic when the goal is + and equality or iff. -/ + useRewrite : Bool := true + /-- When `implicationHyp = true`, interpret the rewrite rule as an implication. -/ + implicationHyp : Bool := false + +/-- +Rewrite `e` using the relation `hrel : x ~ y`, and construct an implication proof +using the `gcongr` tactic to discharge this goal. + +if `forwardImp = true`, we prove that `e → eNew`; otherwise `eNew → e`. + +If `symm = false`, we rewrite `e` to `eNew := e[x/y]`; otherwise `eNew := e[y/x]`. + +The code aligns with `Lean.MVarId.rewrite` as much as possible. +-/ +def _root_.Lean.MVarId.grewrite (goal : MVarId) (e : Expr) (hrel : Expr) + (forwardImp symm : Bool) (config : GRewrite.Config) : MetaM GRewriteResult := + goal.withContext do + goal.checkNotAssigned `grewrite + let hrelType ← instantiateMVars (← inferType hrel) + let maxMVars? ← + if config.implicationHyp then + if let arity + 1 := hrelType.getForallArity then + pure (some arity) + else + throwTacticEx `apply_rw goal m!"invalid implication {hrelType}" + else + pure none + let (newMVars, binderInfos, hrelType) ← + withReducible <| forallMetaTelescopeReducing hrelType maxMVars? + + -- If we can use the normal `rewrite` tactic, we default to using that. + if (hrelType.isAppOfArity ``Iff 2 || hrelType.isAppOfArity ``Eq 3) && config.useRewrite then + let { eNew, eqProof, mvarIds } ← goal.rewrite e hrel symm config.toConfig + let mp := if forwardImp then ``Eq.mp else ``Eq.mpr + let impProof ← mkAppOptM mp #[e, eNew, eqProof] + return { eNew, impProof, mvarIds } + + let hrelIn := hrel + -- check that `hrel` proves a relation + let hrel := mkAppN hrel newMVars + let some (_, lhs, rhs) := GCongr.getRel hrelType | + throwTacticEx `grewrite goal m!"{hrelType} is not a relation" + let (lhs, rhs) := if symm then (rhs, lhs) else (lhs, rhs) + if lhs.getAppFn.isMVar then + throwTacticEx `grewrite goal + m!"pattern is a metavariable{indentExpr lhs}\nfrom relation{indentExpr hrelType}" + -- abstract the occurrences of `lhs` from `e` to get `eAbst` + let e ← instantiateMVars e + let eAbst ← + withConfig (fun oldConfig => { config, oldConfig with }) <| kabstract e lhs config.occs + unless eAbst.hasLooseBVars do + throwTacticEx `grewrite goal + m!"did not find instance of the pattern in the target expression{indentExpr lhs}" + -- construct `eNew` by instantiating `eAbst` with `rhs`. + let eNew := eAbst.instantiate1 rhs + let eNew ← instantiateMVars eNew + -- check that `eNew` is well typed + try + check eNew + catch ex => + throwTacticEx `grewrite goal m!"\ + rewritten expression is not type correct:{indentD eNew}\nError: {ex.toMessageData}\ + \n\n\ + Possible solutions: use grewrite's 'occs' configuration option to limit which occurrences \ + are rewritten, or specify what the rewritten expression should be and use 'gcongr'." + let eNew ← if rhs.hasBinderNameHint then eNew.resolveBinderNameHint else pure eNew + -- construct the implication proof using `gcongr` + let template := eAbst.instantiate1 (← mkFreshExprSyntheticOpaqueMVar default) + let mkImp (e₁ e₂ : Expr) : Expr := .forallE `_a e₁ e₂ .default + let imp := if forwardImp then mkImp e eNew else mkImp eNew e + let gcongrGoal ← mkFreshExprMVar imp + let (_, _, sideGoals) ← gcongrGoal.mvarId!.gcongr template [] (inGRewrite := true) + (mainGoalDischarger := GRewrite.dischargeMain hrel) + -- post-process the metavariables + postprocessAppMVars `grewrite goal newMVars binderInfos + (synthAssignedInstances := !tactic.skipAssignedInstances.get (← getOptions)) + let newMVarIds ← (sideGoals ++ newMVars.map Expr.mvarId!).filterM (not <$> ·.isAssigned) + let otherMVarIds ← getMVarsNoDelayed hrelIn + let otherMVarIds := otherMVarIds.filter (!newMVarIds.contains ·) + let newMVarIds := newMVarIds ++ otherMVarIds + pure { eNew, impProof := ← instantiateMVars gcongrGoal, mvarIds := newMVarIds.toList } + +end Mathlib.Tactic diff --git a/Mathlib/Tactic/GRewrite/Elab.lean b/Mathlib/Tactic/GRewrite/Elab.lean new file mode 100644 index 00000000000000..aab5a4b4ddbac0 --- /dev/null +++ b/Mathlib/Tactic/GRewrite/Elab.lean @@ -0,0 +1,131 @@ +/- +Copyright (c) 2023 Sebastian Zimmer. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sebastian Zimmer, Mario Carneiro, Heather Macbeth, Jovan Gerbscheid +-/ +import Mathlib.Tactic.GRewrite.Core + +/-! + +# The generalized rewriting tactic + +This file defines the tactics that use the backend defined in `Mathlib.Tactic.GRewrite.Core`: +- `grewrite` +- `grw` +- `apply_rw` +- `nth_grewrite` +- `nth_grw` + +-/ + +namespace Mathlib.Tactic + +open Lean Meta Elab Parser Tactic + +/-- Apply the `grewrite` tactic to the current goal. -/ +def grewriteTarget (stx : Syntax) (symm : Bool) (config : GRewrite.Config) : TacticM Unit := do + let goal ← getMainGoal + Term.withSynthesize <| goal.withContext do + let e ← elabTerm stx none true + if e.hasSyntheticSorry then + throwAbortTactic + let goal ← getMainGoal + let target ← goal.getType + let r ← goal.grewrite target e (forwardImp := false) (symm := symm) (config := config) + let mvarNew ← mkFreshExprSyntheticOpaqueMVar r.eNew (← goal.getTag) + goal.assign (mkApp r.impProof mvarNew) + replaceMainGoal (mvarNew.mvarId! :: r.mvarIds) + +/-- Apply the `grewrite` tactic to a local hypothesis. -/ +def grewriteLocalDecl (stx : Syntax) (symm : Bool) (fvarId : FVarId) (config : GRewrite.Config) : + TacticM Unit := withMainContext do + -- Note: we cannot execute `replace` inside `Term.withSynthesize`. + -- See issues #2711 and #2727. + let goal ← getMainGoal + let r ← Term.withSynthesize <| withMainContext do + let e ← elabTerm stx none true + if e.hasSyntheticSorry then + throwAbortTactic + let localDecl ← fvarId.getDecl + goal.grewrite localDecl.type e (forwardImp := true) (symm := symm) (config := config) + let proof := .app (r.impProof) (.fvar fvarId) + let { mvarId, .. } ← goal.replace fvarId proof r.eNew + replaceMainGoal (mvarId :: r.mvarIds) + +/-- Function elaborating `GRewrite.Config`. -/ +declare_config_elab elabGRewriteConfig GRewrite.Config + +/-- +`grewrite [e]` works just like `rewerite [e]`, but `e` can be a relation other than `=` or `↔`. + +For example, +```lean +example (h₁ : a < b) (h₂ : b ≤ c) : a + d ≤ c + d := by + grewrite [h₁, h₂]; rfl + +example (h : a ≡ b [ZMOD n]) : a ^ 2 ≡ b ^ 2 [ZMOD n] := by + grewrite [h]; rfl + +example : (h₁ : a ∣ b) (h₂ : c ∣ a * d) : a ∣ b * d := by + grewrite [h₁] + exact h₂ + +``` +To be able to use `grewrite`, the relevant lemmas need to be tagged with `@[gcongr]`. +To rewrite inside a transitive relation, you can also give it an `IsTrans` instance. +-/ +syntax (name := grewriteSeq) "grewrite" optConfig rwRuleSeq (location)? : tactic + +@[tactic grewriteSeq, inherit_doc grewriteSeq] def evalGRewriteSeq : Tactic := fun stx => do + let cfg ← elabGRewriteConfig stx[1] + let loc := expandOptLocation stx[3] + withRWRulesSeq stx[0] stx[2] fun symm term => do + withLocation loc + (grewriteLocalDecl term symm · cfg) + (grewriteTarget term symm cfg) + (throwTacticEx `grewrite · "did not find instance of the pattern in the current goal") + +/-- +`grw [e]` works just like `rw [e]`, but `e` can be a relation other than `=` or `↔`. + +For example, +```lean +example (h₁ : a < b) (h₂ : b ≤ c) : a + d ≤ c + d := by + grw [h₁, h₂] + +example (h : a ≡ b [ZMOD n]) : a ^ 2 ≡ b ^ 2 [ZMOD n] := by + grw [h] + +example : (h₁ : a ∣ b) (h₂ : c ∣ a * d) : a ∣ b * d := by + grw [h₁] + exact h₂ + +``` +To be able to use `grw`, the relevant lemmas need to be tagged with `@[gcongr]`. +To rewrite inside a transitive relation, you can also give it an `IsTrans` instance. +-/ +macro (name := rwSeq) "grw " c:optConfig s:rwRuleSeq l:(location)? : tactic => + match s with + | `(rwRuleSeq| [$rs,*]%$rbrak) => + -- We show the `rfl` state on `]` + `(tactic| (grewrite $c [$rs,*] $(l)?; with_annotate_state $rbrak (try (with_reducible rfl)))) + | _ => Macro.throwUnsupported + + +/-- `apply_rewrite [rules]` is a shorthand for `grewrite +implicationHyp [rules]`. -/ +macro "apply_rewrite" c:optConfig s:rwRuleSeq loc:(location)? : tactic => do + `(tactic| grewrite $[$(getConfigItems c)]* +implicationHyp $s:rwRuleSeq $(loc)?) + +/-- `apply_rw [rules]` is a shorthand for `grw +implicationHyp [rules]`. -/ +macro (name := applyRwSeq) "apply_rw " c:optConfig s:rwRuleSeq loc:(location)? : tactic => do + `(tactic| grw $[$(getConfigItems c)]* +implicationHyp $s:rwRuleSeq $(loc)?) + +/-- `nth_grewrite` is just like `nth_rewrite`, but for `grewrite`. -/ +macro "nth_grewrite" c:optConfig ppSpace nums:(num)+ s:rwRuleSeq loc:(location)? : tactic => do + `(tactic| grewrite $[$(getConfigItems c)]* (occs := .pos [$[$nums],*]) $s:rwRuleSeq $(loc)?) + +/-- `nth_grw` is just like `nth_rw`, but for `grw`. -/ +macro "nth_grw" c:optConfig ppSpace nums:(num)+ s:rwRuleSeq loc:(location)? : tactic => do + `(tactic| grw $[$(getConfigItems c)]* (occs := .pos [$[$nums],*]) $s:rwRuleSeq $(loc)?) + +end Mathlib.Tactic diff --git a/Mathlib/Tactic/Hint.lean b/Mathlib/Tactic/Hint.lean index 74e419f70a854d..f5189717b6234e 100644 --- a/Mathlib/Tactic/Hint.lean +++ b/Mathlib/Tactic/Hint.lean @@ -6,7 +6,6 @@ Authors: Kim Morrison import Lean.Meta.Tactic.TryThis import Batteries.Linter.UnreachableTactic import Batteries.Control.Nondet.Basic -import Mathlib.Tactic.FailIfNoProgress /-! # The `hint` tactic. diff --git a/Mathlib/Tactic/Linter/DirectoryDependency.lean b/Mathlib/Tactic/Linter/DirectoryDependency.lean index fee8040f3448dd..8bd8c82fc9c0bf 100644 --- a/Mathlib/Tactic/Linter/DirectoryDependency.lean +++ b/Mathlib/Tactic/Linter/DirectoryDependency.lean @@ -143,8 +143,8 @@ def contains (r : NamePrefixRel) (n₁ n₂ : Name) : Bool := (r.find n₁ n₂) def getAllLeft (r : NamePrefixRel) (n : Name) : NameSet := Id.run do let matchingPrefixes := n.prefixes.filter (fun prf ↦ r.containsKey prf) let mut allRules := NameSet.empty - for prfix in matchingPrefixes do - let some rules := RBMap.find? r prfix | unreachable! + for prefix_ in matchingPrefixes do + let some rules := RBMap.find? r prefix_ | unreachable! allRules := allRules.append rules allRules diff --git a/Mathlib/Tactic/NormNum/Prime.lean b/Mathlib/Tactic/NormNum/Prime.lean index 461bbd76ca37e5..f8c92a999e6747 100644 --- a/Mathlib/Tactic/NormNum/Prime.lean +++ b/Mathlib/Tactic/NormNum/Prime.lean @@ -196,42 +196,6 @@ theorem isNat_not_prime {n n' : ℕ} (h : IsNat n n') : ¬n'.Prime → ¬n.Prime return .isTrue q(isNat_prime_2 $pn $r $p2n) core -/- -/-- A partial proof of `factors`. Asserts that `l` is a sorted list of primes, lower bounded by a -prime `p`, which multiplies to `n`. -/ -def FactorsHelper (n p : ℕ) (l : List ℕ) : Prop := - p.Prime → List.Chain (· ≤ ·) p l ∧ (∀ a ∈ l, Nat.Prime a) ∧ List.prod l = n - -theorem factorsHelper_nil (a : ℕ) : FactorsHelper 1 a [] := fun _ => - ⟨List.Chain.nil, by rintro _ ⟨⟩, List.prod_nil⟩ - -theorem factorsHelper_cons' (n m a b : ℕ) (l : List ℕ) (h₁ : b * m = n) (h₂ : a ≤ b) - (h₃ : minFac b = b) (H : FactorsHelper m b l) : FactorsHelper n a (b :: l) := fun pa => - have pb : b.Prime := Nat.prime_def_minFac.2 ⟨le_trans pa.two_le h₂, h₃⟩ - let ⟨f₁, f₂, f₃⟩ := H pb - ⟨List.Chain.cons h₂ f₁, - fun c h => (List.eq_or_mem_of_mem_cons h).elim (fun e => e.symm ▸ pb) (f₂ _), - by rw [List.prod_cons, f₃, h₁]⟩ - -theorem factorsHelper_cons (n m a b : ℕ) (l : List ℕ) (h₁ : b * m = n) (h₂ : a < b) - (h₃ : minFac b = b) (H : FactorsHelper m b l) : FactorsHelper n a (b :: l) := - factorsHelper_cons' _ _ _ _ _ h₁ h₂.le h₃ H - -theorem factorsHelper_sn (n a : ℕ) (h₁ : a < n) (h₂ : minFac n = n) : FactorsHelper n a [n] := - factorsHelper_cons _ _ _ _ _ (mul_one _) h₁ h₂ (factorsHelper_nil _) - -theorem factorsHelper_same (n m a : ℕ) (l : List ℕ) (h : a * m = n) (H : FactorsHelper m a l) : - FactorsHelper n a (a :: l) := fun pa => - factorsHelper_cons' _ _ _ _ _ h le_rfl (Nat.prime_def_minFac.1 pa).2 H pa - -theorem factorsHelper_same_sn (a : ℕ) : FactorsHelper a a [a] := - factorsHelper_same _ _ _ _ (mul_one _) (factorsHelper_nil _) - -theorem factorsHelper_end (n : ℕ) (l : List ℕ) (H : FactorsHelper n 2 l) : Nat.factors n = l := - let ⟨h₁, h₂, h₃⟩ := H Nat.prime_two - have := List.chain'_iff_pairwise.1 (@List.Chain'.tail _ _ (_ :: _) h₁) - (List.eq_of_perm_of_sorted (Nat.factors_unique h₃ h₂) this (Nat.factors_sorted _)).symm --/ end NormNum diff --git a/Mathlib/Tactic/NthRewrite.lean b/Mathlib/Tactic/NthRewrite.lean index 0886a91ce762cf..8df49ba10e25ff 100644 --- a/Mathlib/Tactic/NthRewrite.lean +++ b/Mathlib/Tactic/NthRewrite.lean @@ -5,7 +5,6 @@ Authors: Moritz Doll -/ import Mathlib.Init -import Lean.Elab.Tactic.Rewrite /-! # `nth_rewrite` tactic @@ -69,21 +68,8 @@ h: a = a + b This new instance of `a` also turns out to be the third _occurrence_ of `a`. Therefore, the next `nth_rewrite` with `h` rewrites this `a`. -/ -syntax (name := nthRewriteSeq) "nth_rewrite" optConfig ppSpace num+ rwRuleSeq (location)? : tactic - -@[inherit_doc nthRewriteSeq, tactic nthRewriteSeq] def evalNthRewriteSeq : Tactic := fun stx => do - match stx with - | `(tactic| nth_rewrite $cfg:optConfig $[$n]* $_rules:rwRuleSeq $[$loc]?) => - let cfg ← elabRewriteConfig cfg - let loc := expandOptLocation (mkOptionalNode loc) - let occ := Occurrences.pos (n.map TSyntax.getNat).toList - let cfg := { cfg with occs := occ } - withRWRulesSeq stx[0] stx[3] fun symm term => do - withLocation loc - (rewriteLocalDecl term symm · cfg) - (rewriteTarget term symm cfg) - (throwTacticEx `nth_rewrite · "did not find instance of the pattern in the current goal") - | _ => throwUnsupportedSyntax +macro "nth_rewrite" c:optConfig ppSpace nums:(num)+ s:rwRuleSeq loc:(location)? : tactic => do + `(tactic| rewrite $[$(getConfigItems c)]* (occs := .pos [$[$nums],*]) $s:rwRuleSeq $(loc)?) /-- `nth_rw` is a variant of `rw` that only changes the `n₁, ..., nₖ`ᵗʰ _occurrence_ of the expression @@ -138,13 +124,8 @@ the next `nth_rw` with `h` rewrites this `a`. Further, `nth_rw` will close the remaining goal with `rfl` if possible. -/ -macro (name := nthRwSeq) "nth_rw" c:optConfig ppSpace n:num+ s:rwRuleSeq l:(location)? : tactic => - -- Note: This is a direct copy of `nth_rw` from core. - match s with - | `(rwRuleSeq| [$rs,*]%$rbrak) => - -- We show the `rfl` state on `]` - `(tactic| (nth_rewrite $c:optConfig $[$n]* [$rs,*] $(l)?; with_annotate_state $rbrak - (try (with_reducible rfl)))) - | _ => Macro.throwUnsupported +macro "nth_rw" c:optConfig ppSpace nums:(num)+ s:rwRuleSeq loc:(location)? : tactic => do + `(tactic| rw $[$(getConfigItems c)]* (occs := .pos [$[$nums],*]) $s:rwRuleSeq $(loc)?) + end Mathlib.Tactic diff --git a/Mathlib/Tactic/Positivity/Core.lean b/Mathlib/Tactic/Positivity/Core.lean index b476bf2f7691e5..326f91a50bf650 100644 --- a/Mathlib/Tactic/Positivity/Core.lean +++ b/Mathlib/Tactic/Positivity/Core.lean @@ -448,6 +448,12 @@ end Positivity end Mathlib.Tactic +/-! +We set up `positivity` as a first-pass discharger for `gcongr` side goals. +-/ + +macro_rules | `(tactic| gcongr_discharger) => `(tactic| positivity) + /-! We register `positivity` with the `hint` tactic. -/ diff --git a/Mathlib/Tactic/Simproc/Factors.lean b/Mathlib/Tactic/Simproc/Factors.lean new file mode 100644 index 00000000000000..0fa679126a47a1 --- /dev/null +++ b/Mathlib/Tactic/Simproc/Factors.lean @@ -0,0 +1,162 @@ +/- +Copyright (c) 2021 Mario Carneiro. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Mario Carneiro, Eric Wieser +-/ +import Mathlib.Data.Nat.Factors +import Mathlib.Tactic.NormNum.Prime + +/-! +# `simproc` for `Nat.primeFactorsList` + +Note that since `norm_num` can only produce numerals, +we can't register this as a `norm_num` extension. +-/ + +open Nat + +namespace Mathlib.Meta.Simproc +open Mathlib.Meta.NormNum + +/-- A proof of the partial computation of `primeFactorsList`. +Asserts that `l` is a sorted list of primes multiplying to `n` and lower bounded by a prime `p`. -/ +def FactorsHelper (n p : ℕ) (l : List ℕ) : Prop := + p.Prime → l.Chain (· ≤ ·) p ∧ (∀ a ∈ l, Nat.Prime a) ∧ l.prod = n + +/-! The argument explicitness in this section is chosen to make only the numerals in the factors +list appear in the proof term. -/ + +theorem FactorsHelper.nil {a : ℕ} : FactorsHelper 1 a [] := fun _ => + ⟨.nil, List.forall_mem_nil _, List.prod_nil⟩ + +theorem FactorsHelper.cons_of_le + {n m : ℕ} (a : ℕ) {b : ℕ} {l : List ℕ} (h₁ : IsNat (b * m) n) (h₂ : a ≤ b) + (h₃ : minFac b = b) (H : FactorsHelper m b l) : FactorsHelper n a (b :: l) := fun pa => + have pb : b.Prime := Nat.prime_def_minFac.2 ⟨le_trans pa.two_le h₂, h₃⟩ + let ⟨f₁, f₂, f₃⟩ := H pb + ⟨List.Chain.cons h₂ f₁, + fun _ h => (List.eq_or_mem_of_mem_cons h).elim (fun e => e.symm ▸ pb) (f₂ _), + by rw [List.prod_cons, f₃, h₁.out, cast_id]⟩ + +theorem FactorsHelper.cons + {n m : ℕ} {a : ℕ} (b : ℕ) {l : List ℕ} (h₁ : IsNat (b * m) n) (h₂ : Nat.blt a b) + (h₃ : IsNat (minFac b) b) (H : FactorsHelper m b l) : FactorsHelper n a (b :: l) := + H.cons_of_le _ h₁ (Nat.blt_eq.mp h₂).le h₃.out + +theorem FactorsHelper.singleton (n : ℕ) {a : ℕ} (h₁ : Nat.blt a n) (h₂ : IsNat (minFac n) n) : + FactorsHelper n a [n] := + FactorsHelper.nil.cons _ ⟨mul_one _⟩ h₁ h₂ + +theorem FactorsHelper.cons_self {n m : ℕ} (a : ℕ) {l : List ℕ} + (h : IsNat (a * m) n) (H : FactorsHelper m a l) : + FactorsHelper n a (a :: l) := fun pa => + H.cons_of_le _ h le_rfl (Nat.prime_def_minFac.1 pa).2 pa + +theorem FactorsHelper.singleton_self (a : ℕ) : FactorsHelper a a [a] := + FactorsHelper.nil.cons_self _ ⟨mul_one _⟩ + +theorem FactorsHelper.primeFactorsList_eq {n : ℕ} {l : List ℕ} (H : FactorsHelper n 2 l) : + Nat.primeFactorsList n = l := + let ⟨h₁, h₂, h₃⟩ := H Nat.prime_two + have := List.chain'_iff_pairwise.1 (@List.Chain'.tail _ _ (_ :: _) h₁) + (List.eq_of_perm_of_sorted + (Nat.primeFactorsList_unique h₃ h₂) this (Nat.primeFactorsList_sorted _)).symm + +open Lean Elab Tactic Qq + +/-- Given `n` and `a` (in expressions `en` and `ea`) corresponding to literal numerals +(in `enl` and `eal`), returns `(l, ⊢ factorsHelper n a l)`. -/ +private partial def evalPrimeFactorsListAux + {en enl : Q(ℕ)} {ea eal : Q(ℕ)} (ehn : Q(IsNat $en $enl)) (eha : Q(IsNat $ea $eal)) : + MetaM ((l : Q(List ℕ)) × Q(FactorsHelper $en $ea $l)) := do + /- + In this function we will use the convention that all `e` prefixed variables (proofs or otherwise) + contain `Expr`s. The variables starting with `h` are proofs about the _meta_ code; + these will not actually be used in the construction of the proof, and are simply used to help the + reader reason about why the proof construction is correct. + -/ + let n := enl.natLit! + let ⟨hn0⟩ ← if h : 0 < n then pure <| PLift.up h else + throwError m!"{enl} must be positive" + let a := eal.natLit! + let b := n.minFac + let ⟨hab⟩ ← if h : a ≤ b then pure <| PLift.up h else + throwError m!"{q($eal < $(enl).minFac)} does not hold" + if h_bn : b < n then + -- the factor is less than `n`, so we are not done; remove it to get `m` + let m := n / b + have em : Q(ℕ) := mkRawNatLit m + have ehm : Q(IsNat (OfNat.ofNat $em) $em) := q(⟨rfl⟩) + if h_ba_eq : b = a then + -- if the factor is our minimum `a`, then recurse without changing the minimum + have eh : Q($eal * $em = $en) := + have : a * m = n := by simp [m, b, ← h_ba_eq, Nat.mul_div_cancel' (minFac_dvd _)] + (q(Eq.refl $en) : Expr) + let ehp₁ := q(isNat_mul rfl $eha $ehm $eh) + let ⟨el, ehp₂⟩ ← evalPrimeFactorsListAux ehm eha + pure ⟨q($ea :: $el), q(($ehp₂).cons_self _ $ehp₁)⟩ + else + -- Otherwise when we recurse, we should use `b` as the new minimum factor. Note that + -- we must use `evalMinFac.core` to get a proof that `b` is what we computed it as. + have eb : Q(ℕ) := mkRawNatLit b + have ehb : Q(IsNat (OfNat.ofNat $eb) $eb) := q(⟨rfl⟩) + have ehbm : Q($eb * $em = $en) := + have : b * m = n := Nat.mul_div_cancel' (minFac_dvd _) + (q(Eq.refl $en) : Expr) + have ehp₁ := q(isNat_mul rfl $ehb $ehm $ehbm) + have ehp₂ : Q(Nat.blt $ea $eb = true) := + have : a < b := lt_of_le_of_ne' hab h_ba_eq + (q(Eq.refl (true)) : Expr) + let .isNat _ lit ehp₃ ← evalMinFac.core q($eb) q(inferInstance) q($eb) ehb b | failure + assertInstancesCommute + have : $lit =Q $eb := ⟨⟩ + let ⟨l, p₄⟩ ← evalPrimeFactorsListAux ehm ehb + pure ⟨q($eb :: $l), q(($p₄).cons _ $ehp₁ $ehp₂ $ehp₃ )⟩ + else + -- the factor is our number itself, so we are done + have hbn_eq : b = n := (minFac_le hn0).eq_or_lt.resolve_right h_bn + if hba : b = a then + have eh : Q($en = $ea) := + have : n = a := hbn_eq.symm.trans hba + (q(Eq.refl $en) : Expr) + pure ⟨q([$ea]), q($eh ▸ FactorsHelper.singleton_self $ea)⟩ + else do + let eh_a_lt_n : Q(Nat.blt $ea $en = true) := + have : a < n := by omega + (q(Eq.refl true) : Expr) + let .isNat _ lit ehn_minFac ← evalMinFac.core q($en) q(inferInstance) q($enl) ehn n | failure + have : $lit =Q $en := ⟨⟩ + assertInstancesCommute + pure ⟨q([$en]), q(FactorsHelper.singleton $en $eh_a_lt_n $ehn_minFac)⟩ + +/-- Given a natural number `n`, returns `(l, ⊢ Nat.primeFactorsList n = l)`. -/ +def evalPrimeFactorsList + {en enl : Q(ℕ)} (hn : Q(IsNat $en $enl)) : + MetaM ((l : Q(List ℕ)) × Q(Nat.primeFactorsList $en = $l)) := do + match enl.natLit! with + | 0 => + have _ : $enl =Q nat_lit 0 := ⟨⟩ + have hen : Q($en = 0) := q($(hn).out) + return ⟨_, q($hen ▸ Nat.primeFactorsList_zero)⟩ + | 1 => + let _ : $enl =Q nat_lit 1 := ⟨⟩ + have hen : Q($en = 1) := q($(hn).out) + return ⟨_, q($hen ▸ Nat.primeFactorsList_one)⟩ + | _ => do + have h2 : Q(IsNat 2 (nat_lit 2)) := q(⟨Eq.refl (nat_lit 2)⟩) + let ⟨l, p⟩ ← evalPrimeFactorsListAux hn h2 + return ⟨l, q(($p).primeFactorsList_eq)⟩ + +end Mathlib.Meta.Simproc + +open Qq Mathlib.Meta.Simproc Mathlib.Meta.NormNum + +/-- A simproc for terms of the form `Nat.primeFactorsList (OfNat.ofNat n)`. -/ +simproc Nat.primeFactorsList_ofNat (Nat.primeFactorsList _) := .ofQ fun u α e => do + match u, α, e with + | 1, ~q(List ℕ), ~q(Nat.primeFactorsList (OfNat.ofNat $n)) => + let hn : Q(IsNat (OfNat.ofNat $n) $n) := q(⟨rfl⟩) + let ⟨l, p⟩ ← evalPrimeFactorsList hn + return .done <| .mk q($l) <| some q($p) + | _ => + return .continue diff --git a/Mathlib/Tactic/Widget/GCongr.lean b/Mathlib/Tactic/Widget/GCongr.lean index bd389f9f33d892..af7b98fa97b5c9 100644 --- a/Mathlib/Tactic/Widget/GCongr.lean +++ b/Mathlib/Tactic/Widget/GCongr.lean @@ -3,6 +3,7 @@ Copyright (c) 2023 Patrick Massot. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Patrick Massot -/ +import Batteries.Lean.Position import Mathlib.Tactic.Widget.SelectPanelUtils import Mathlib.Tactic.GCongr diff --git a/Mathlib/Topology/Algebra/Algebra.lean b/Mathlib/Topology/Algebra/Algebra.lean index d48dcdf88ffc74..b599f4461c7a89 100644 --- a/Mathlib/Topology/Algebra/Algebra.lean +++ b/Mathlib/Topology/Algebra/Algebra.lean @@ -441,8 +441,6 @@ theorem coe_prodMap' {D : Type*} [Semiring D] [TopologicalSpace D] [Algebra R D] def prodEquiv : (A →A[R] B) × (A →A[R] C) ≃ (A →A[R] B × C) where toFun f := f.1.prod f.2 invFun f := ⟨(fst _ _ _).comp f, (snd _ _ _).comp f⟩ - left_inv f := by ext <;> rfl - right_inv f := by ext <;> rfl end prod diff --git a/Mathlib/Topology/Algebra/ContinuousMonoidHom.lean b/Mathlib/Topology/Algebra/ContinuousMonoidHom.lean index 9e7abbe2bc2b3b..c8b974fcf47ccb 100644 --- a/Mathlib/Topology/Algebra/ContinuousMonoidHom.lean +++ b/Mathlib/Topology/Algebra/ContinuousMonoidHom.lean @@ -417,6 +417,7 @@ def symm (cme : M ≃ₜ* N) : N ≃ₜ* M := continuous_toFun := cme.continuous_invFun continuous_invFun := cme.continuous_toFun } initialize_simps_projections ContinuousMulEquiv (toFun → apply, invFun → symm_apply) +initialize_simps_projections ContinuousAddEquiv (toFun → apply, invFun → symm_apply) @[to_additive] theorem invFun_eq_symm {f : M ≃ₜ* N} : f.invFun = f.symm := rfl diff --git a/Mathlib/Topology/Algebra/InfiniteSum/UniformOn.lean b/Mathlib/Topology/Algebra/InfiniteSum/UniformOn.lean index af25db8ca665d5..835c7993177e01 100644 --- a/Mathlib/Topology/Algebra/InfiniteSum/UniformOn.lean +++ b/Mathlib/Topology/Algebra/InfiniteSum/UniformOn.lean @@ -65,7 +65,23 @@ lemma hasProdUniformlyOn_iff_tendstoUniformlyOn : HasProdUniformlyOn f g 𝔖 UniformOnFun.tendsto_iff_tendstoUniformlyOn @[to_additive] -lemma HasProdUniformlyOn.tendstoUniformlyOn_finset_range +lemma HasProdUniformlyOn.congr {f' : ι → β → α} + (h : HasProdUniformlyOn f g 𝔖) + (hff' : ∀ s ∈ 𝔖, ∀ᶠ (n : Finset ι) in atTop, + Set.EqOn (fun b ↦ ∏ i ∈ n, f i b) (fun b ↦ ∏ i ∈ n, f' i b) s) : + HasProdUniformlyOn f' g 𝔖 := by + rw [hasProdUniformlyOn_iff_tendstoUniformlyOn] at * + exact fun s hs ↦ TendstoUniformlyOn.congr (h s hs) (hff' s hs) + +@[to_additive] +lemma HasProdUniformlyOn.congr_right {g' : β → α} + (h : HasProdUniformlyOn f g 𝔖) (hgg' : ∀ s ∈ 𝔖, Set.EqOn g g' s) : + HasProdUniformlyOn f g' 𝔖 := by + rw [hasProdUniformlyOn_iff_tendstoUniformlyOn] at * + exact fun s hs ↦ TendstoUniformlyOn.congr_right (h s hs) (hgg' s hs) + +@[to_additive] +lemma HasProdUniformlyOn.tendstoUniformlyOn_finsetRange {f : ℕ → β → α} (h : HasProdUniformlyOn f g 𝔖) (hs : s ∈ 𝔖) : TendstoUniformlyOn (fun N b ↦ ∏ i ∈ Finset.range N, f i b) g atTop s := by rw [hasProdUniformlyOn_iff_tendstoUniformlyOn] at h @@ -208,4 +224,13 @@ theorem HasProdLocallyUniformlyOn.tprod_eqOn [T2Space α] (h : HasProdLocallyUniformlyOn f g s) : Set.EqOn (∏' i, f i ·) g s := fun _ hx ↦ (h.hasProd hx).tprod_eq +@[to_additive] +lemma HasProdLocallyUniformlyOn.tendstoLocallyUniformlyOn_finsetRange + {f : ℕ → β → α} (h : HasProdLocallyUniformlyOn f g s) : + TendstoLocallyUniformlyOn (fun N b ↦ ∏ i ∈ Finset.range N, f i b) g atTop s := by + rw [hasProdLocallyUniformlyOn_iff_tendstoLocallyUniformlyOn] at h + intro v hv r hr + obtain ⟨t, ht, htr⟩ := h v hv r hr + exact ⟨t, ht, Filter.tendsto_finset_range.eventually htr⟩ + end LocallyUniformlyOn diff --git a/Mathlib/Topology/Algebra/Module/Alternating/Basic.lean b/Mathlib/Topology/Algebra/Module/Alternating/Basic.lean index 7ea2fbb251f1e8..5c6fa78ccbf8c9 100644 --- a/Mathlib/Topology/Algebra/Module/Alternating/Basic.lean +++ b/Mathlib/Topology/Algebra/Module/Alternating/Basic.lean @@ -285,7 +285,6 @@ def ofSubsingleton [Subsingleton ι] (i : ι) : { AlternatingMap.ofSubsingleton R M N i f with toContinuousMultilinearMap := ContinuousMultilinearMap.ofSubsingleton R M N i f } invFun f := (ContinuousMultilinearMap.ofSubsingleton R M N i).symm f.1 - left_inv _ := rfl right_inv _ := toContinuousMultilinearMap_injective <| (ContinuousMultilinearMap.ofSubsingleton R M N i).apply_symm_apply _ @@ -383,8 +382,6 @@ def piEquiv {ι' : Type*} {N : ι' → Type*} [∀ i, AddCommMonoid (N i)] [∀ [∀ i, Module R (N i)] : (∀ i, M [⋀^ι]→L[R] N i) ≃ M [⋀^ι]→L[R] ∀ i, N i where toFun := pi invFun f i := (ContinuousLinearMap.proj i : _ →L[R] N i).compContinuousAlternatingMap f - left_inv f := by ext; rfl - right_inv f := by ext; rfl /-- In the specific case of continuous alternating maps on spaces indexed by `Fin (n+1)`, where one can build an element of `Π(i : Fin (n+1)), M i` using `cons`, one can express directly the diff --git a/Mathlib/Topology/Algebra/Module/Equiv.lean b/Mathlib/Topology/Algebra/Module/Equiv.lean index 6551ab65a497f6..57e7e906ef4b82 100644 --- a/Mathlib/Topology/Algebra/Module/Equiv.lean +++ b/Mathlib/Topology/Algebra/Module/Equiv.lean @@ -161,6 +161,9 @@ instance continuousSemilinearEquivClass : map_continuous := continuous_toFun inv_continuous := continuous_invFun +@[simp] +theorem coe_mk (e : M₁ ≃ₛₗ[σ₁₂] M₂) (a b) : ⇑(ContinuousLinearEquiv.mk e a b) = e := rfl + theorem coe_apply (e : M₁ ≃SL[σ₁₂] M₂) (b : M₁) : (e : M₁ →SL[σ₁₂] M₂) b = e b := rfl @@ -291,14 +294,27 @@ protected def symm (e : M₁ ≃SL[σ₁₂] M₂) : M₂ ≃SL[σ₂₁] M₁ : continuous_invFun := e.continuous_toFun } @[simp] -theorem symm_toLinearEquiv (e : M₁ ≃SL[σ₁₂] M₂) : e.symm.toLinearEquiv = e.toLinearEquiv.symm := by - ext +theorem toLinearEquiv_symm (e : M₁ ≃SL[σ₁₂] M₂) : e.symm.toLinearEquiv = e.toLinearEquiv.symm := rfl +@[deprecated (since := "2025-06-08")] alias symm_toLinearEquiv := toLinearEquiv_symm + @[simp] +theorem coe_symm_toLinearEquiv (e : M₁ ≃SL[σ₁₂] M₂) : ⇑e.toLinearEquiv.symm = e.symm := + rfl + +@[simp] +theorem toHomeomorph_symm (e : M₁ ≃SL[σ₁₂] M₂) : e.symm.toHomeomorph = e.toHomeomorph.symm := + rfl + +@[deprecated "use instead `toHomeomorph_symm`, in the reverse direction" (since := "2025-06-08")] theorem symm_toHomeomorph (e : M₁ ≃SL[σ₁₂] M₂) : e.toHomeomorph.symm = e.symm.toHomeomorph := rfl +@[simp] +theorem coe_symm_toHomeomorph (e : M₁ ≃SL[σ₁₂] M₂) : ⇑e.toHomeomorph.symm = e.symm := + rfl + /-- See Note [custom simps projection]. We need to specify this projection explicitly in this case, because it is a composition of multiple projections. -/ def Simps.apply (h : M₁ ≃SL[σ₁₂] M₂) : M₁ → M₂ := @@ -695,12 +711,6 @@ equivalent to the type of continuous linear equivalences between `M` and itself. def unitsEquiv : (M →L[R] M)ˣ ≃* M ≃L[R] M where toFun := ofUnit invFun := toUnit - left_inv f := by - ext - rfl - right_inv f := by - ext - rfl map_mul' x y := by ext rfl @@ -1082,6 +1092,11 @@ alias ring_inverse_eq_map_inverse := ringInverse_eq_inverse rw [← ringInverse_eq_inverse] exact Ring.inverse_one _ +/-- Composition of a map on a product with the exchange of the product factors -/ +theorem coprod_comp_prodComm [ContinuousAdd M] (f : M₂ →L[R] M) (g : M₃ →L[R] M) : + f.coprod g ∘L ContinuousLinearEquiv.prodComm R M₃ M₂ = g.coprod f := by + ext <;> simp + end ContinuousLinearMap namespace Submodule diff --git a/Mathlib/Topology/Algebra/Module/FiniteDimension.lean b/Mathlib/Topology/Algebra/Module/FiniteDimension.lean index d82928a60265fd..bd8264cf5ebbdb 100644 --- a/Mathlib/Topology/Algebra/Module/FiniteDimension.lean +++ b/Mathlib/Topology/Algebra/Module/FiniteDimension.lean @@ -279,7 +279,6 @@ def toContinuousLinearMap : (E →ₗ[𝕜] F') ≃ₗ[𝕜] E →L[𝕜] F' whe invFun := (↑) map_add' _ _ := rfl map_smul' _ _ := rfl - left_inv _ := rfl right_inv _ := ContinuousLinearMap.coe_injective rfl /-- Algebra equivalence between the linear maps and continuous linear maps on a finite dimensional diff --git a/Mathlib/Topology/Algebra/Module/LinearMapPiProd.lean b/Mathlib/Topology/Algebra/Module/LinearMapPiProd.lean index 0c0cb3744fbde6..351e66f7361a1d 100644 --- a/Mathlib/Topology/Algebra/Module/LinearMapPiProd.lean +++ b/Mathlib/Topology/Algebra/Module/LinearMapPiProd.lean @@ -254,8 +254,6 @@ variable def prodEquiv : (M →L[R] M₂) × (M →L[R] M₃) ≃ (M →L[R] M₂ × M₃) where toFun f := f.1.prod f.2 invFun f := ⟨(fst _ _ _).comp f, (snd _ _ _).comp f⟩ - left_inv f := by ext <;> rfl - right_inv f := by ext <;> rfl theorem prod_ext_iff {f g : M × M₂ →L[R] M₃} : f = g ↔ f.comp (inl _ _ _) = g.comp (inl _ _ _) ∧ f.comp (inr _ _ _) = g.comp (inr _ _ _) := by diff --git a/Mathlib/Topology/Algebra/Module/LinearPMap.lean b/Mathlib/Topology/Algebra/Module/LinearPMap.lean index c578e7d10f8c38..80cf2033d02a26 100644 --- a/Mathlib/Topology/Algebra/Module/LinearPMap.lean +++ b/Mathlib/Topology/Algebra/Module/LinearPMap.lean @@ -177,8 +177,7 @@ theorem closure_inverse_graph (hf : LinearMap.ker f.toFun = ⊥) (hf' : f.IsClos have h1 := Set.image_equiv_eq_preimage_symm f.graph (LinearEquiv.prodComm R E F).toEquiv have h2 := Set.image_equiv_eq_preimage_symm (_root_.closure f.graph) (LinearEquiv.prodComm R E F).toEquiv - simp only [LinearEquiv.coe_toEquiv, LinearEquiv.prodComm_apply, - LinearEquiv.coe_toEquiv_symm] at h1 h2 + simp only [LinearEquiv.coe_toEquiv, LinearEquiv.prodComm_apply] at h1 h2 rw [h1, h2] apply continuous_swap.closure_preimage_subset diff --git a/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean b/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean index e7976713db7572..32f17a64a3b9e1 100644 --- a/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean +++ b/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean @@ -257,7 +257,6 @@ def ofSubsingleton [Subsingleton ι] (i : ι) : (map_continuous f).comp (continuous_apply i)⟩ invFun f := ⟨(MultilinearMap.ofSubsingleton R M₂ M₃ i).symm f.toMultilinearMap, (map_continuous f).comp <| continuous_pi fun _ ↦ continuous_id⟩ - left_inv _ := rfl right_inv f := toMultilinearMap_injective <| (MultilinearMap.ofSubsingleton R M₂ M₃ i).apply_symm_apply f.toMultilinearMap @@ -307,8 +306,6 @@ def prodEquiv : toFun f := f.1.prod f.2 invFun f := ((ContinuousLinearMap.fst _ _ _).compContinuousMultilinearMap f, (ContinuousLinearMap.snd _ _ _).compContinuousMultilinearMap f) - left_inv _ := rfl - right_inv _ := rfl theorem prod_ext_iff {f g : ContinuousMultilinearMap R M₁ (M₂ × M₃)} : f = g ↔ (ContinuousLinearMap.fst _ _ _).compContinuousMultilinearMap f = @@ -355,8 +352,6 @@ def piEquiv {ι' : Type*} {M' : ι' → Type*} [∀ i, AddCommMonoid (M' i)] (∀ i, ContinuousMultilinearMap R M₁ (M' i)) ≃ ContinuousMultilinearMap R M₁ (∀ i, M' i) where toFun := ContinuousMultilinearMap.pi invFun f i := (ContinuousLinearMap.proj i : _ →L[R] M' i).compContinuousMultilinearMap f - left_inv _ := rfl - right_inv _ := rfl /-- An equivalence of the index set defines an equivalence between the spaces of continuous multilinear maps. This is the forward map of this equivalence. -/ diff --git a/Mathlib/Topology/Algebra/RestrictedProduct.lean b/Mathlib/Topology/Algebra/RestrictedProduct.lean index ca57404ecd3798..6a9bae218318c9 100644 --- a/Mathlib/Topology/Algebra/RestrictedProduct.lean +++ b/Mathlib/Topology/Algebra/RestrictedProduct.lean @@ -1,1022 +1,4 @@ -/- -Copyright (c) 2025 Anatole Dedecker. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Anatole Dedecker --/ -import Mathlib.Topology.Algebra.Group.Pointwise -import Mathlib.Topology.Algebra.Ring.Basic +import Mathlib.Topology.Algebra.RestrictedProduct.Basic +import Mathlib.Topology.Algebra.RestrictedProduct.TopologicalSpace -/-! -# Restricted products of sets, groups and rings, and their topology - -We define the **restricted product** of `R : ι → Type*` of types, relative to -a family of subsets `A : (i : ι) → Set (R i)` and a filter `𝓕 : Filter ι`. This -is the set of all `x : Π i, R i` such that the set `{j | x j ∈ A j}` belongs to `𝓕`. -We denote it by `Πʳ i, [R i, A i]_[𝓕]`. - -The main case of interest, which we shall refer to as the "classical restricted product", -is that of `𝓕 = cofinite`. Recall that this is the filter of all subsets of `ι`, which are -*cofinite* in the sense that they have finite complement. -Hence, the associated restricted product is the set of all `x : Π i, R i` such that -`x j ∈ A j` for all but finitely many `j`s. We denote it simply by `Πʳ i, [R i, A i]`. - -Another notable case is that of the principal filter `𝓕 = 𝓟 s` corresponding to some subset `s` -of `ι`. The associated restricted product `Πʳ i, [R i, A i]_[𝓟 s]` is the set of all -`x : Π i, R i` such that `x j ∈ A j` for all `j ∈ s`. Put another way, this is just -`(Π i ∈ s, A i) × (Π i ∉ s, R i)`, modulo the obvious isomorphism. - -We endow these types with the obvious algebraic structures, as well as their natural topology, -which we describe below. We also show various compatibility results. - -In particular, with the theory of adeles in mind, we show that if each `R i` is a locally compact -topological ring with open subring `A i`, and if all but finitely many of the `A i`s are also -compact, then `Πʳ i, [R i, A i]` is a locally compact topological ring. - -## Main definitions - -* `RestrictedProduct`: the restricted product of a family `R` of types, relative to a family `A` of - subsets and a filter `𝓕` on the indexing set. This is denoted `Πʳ i, [R i, A i]_[𝓕]`, - or simply `Πʳ i, [R i, A i]` when `𝓕 = cofinite`. -* `RestrictedProduct.instDFunLike`: interpret an element of `Πʳ i, [R i, A i]_[𝓕]` as an element - of `Π i, R i` using the `DFunLike` machinery. -* `RestrictedProduct.structureMap`: the inclusion map from `Π i, A i` to `Πʳ i, [R i, A i]_[𝓕]`. -* `RestrictedProduct.topologicalSpace`: the `TopologicalSpace` instance on `Πʳ i, [R i, A i]_[𝓕]`. - -## Topology on the restricted product - -The topology on the restricted product `Πʳ i, [R i, A i]_[𝓕]` is defined in the following way: -1. If `𝓕` is some principal filter `𝓟 s`, recall that `Πʳ i, [R i, A i]_[𝓟 s]` is canonically -identified with `(Π i ∈ s, A i) × (Π i ∉ s, R i)`. We endow it with the product topology, -which is also the topology induced from the full product `Π i, R i`. -2. In general, we note that `𝓕` is the infimum of the principal filters coarser than `𝓕`. We -then endow `Πʳ i, [R i, A i]_[𝓕]` with the inductive limit / final topology associated to the -inclusion maps `Πʳ i, [R i, A i]_[𝓟 s] → Πʳ i, [R i, A i]_[𝓕]` where `𝓕 ≤ 𝓟 s`. - -In particular: -* On the classical restricted product, with respect to the cofinite filter, this corresponds to - taking the inductive limit of the `Πʳ i, [R i, A i]_[𝓟 s]` over all *cofinite* sets `s : Set ι`. -* If `𝓕 = 𝓟 s` is a principal filter, this second step clearly does not change the topology, since - `s` belongs to the indexing set of the inductive limit. - -Taking advantage of that second remark, we do not actually declare an instance specific to -principal filters. Instead, we provide directly the general instance (corresponding to step 2 above) -as `RestrictedProduct.topologicalSpace`. We then prove that, for a principal filter, the -map to the full product is an inducing (`RestrictedProduct.isEmbedding_coe_of_principal`), -and that the topology for a general `𝓕` is indeed the expected inductive limit -(`RestrictedProduct.topologicalSpace_eq_iSup`). - -## Main statements - -* `RestrictedProduct.isEmbedding_coe_of_principal`: for any set `S`, `Πʳ i, [R i, A i]_[𝓟 S]` - is endowed with the subset topology coming from `Π i, R i`. -* `RestrictedProduct.topologicalSpace_eq_iSup`: the topology on `Πʳ i, [R i, A i]_[𝓕]` is the - inductive limit / final topology associated to the natural maps - `Πʳ i, [R i, A i]_[𝓟 S] → Πʳ i, [R i, A i]_[𝓕]`, where `𝓕 ≤ 𝓟 S`. -* `RestrictedProduct.continuous_dom`: a map from `Πʳ i, [R i, A i]_[𝓕]` is continuous -*if and only if* its restriction to each `Πʳ i, [R i, A i]_[𝓟 s]` (with `𝓕 ≤ 𝓟 s`) is continuous. -* `RestrictedProduct.continuous_dom_prod_left`: assume that each `A i` is an **open** subset of -`R i`. Then, for any topological space `Y`, a map from `Y × Πʳ i, [R i, A i]` is continuous -*if and only if* its restriction to each `Y × Πʳ i, [R i, A i]_[𝓟 S]` (with `S` cofinite) -is continuous. - -* `RestrictedProduct.isTopologicalGroup`: if each `R i` is a topological group and each `A i` is an - open subgroup of `R i`, then `Πʳ i, [R i, A i]` is a topological group. -* `RestrictedProduct.isTopologicalRing`: if each `R i` is a topological ring and each `A i` is an - open subring of `R i`, then `Πʳ i, [R i, A i]` is a topological ring. -* `RestrictedProduct.continuousSMul`: if some topological monoid `G` acts on each `M i`, and each - `A i` is stable for that action, then the natural action of `G` on `Πʳ i, [M i, A i]` is also - continuous. In particular, if each `M i` is a topological `R`-module and each `A i` is an open - sub-`R`-module of `M i`, then `Πʳ i, [M i, A i]` is a topological `R`-module. - -* `RestrictedProduct.weaklyLocallyCompactSpace_of_cofinite`: if each `R i` is weakly locally - compact, each `A i` is open, and all but finitely many `A i`s are also compact, then the - restricted product `Πʳ i, [R i, A i]` is weakly locally compact. -* `RestrictedProduct.locallyCompactSpace_of_group`: assume that each `R i` is a locally compact - group with `A i` an open subgroup. Assume also that all but finitely many `A i`s are compact. - Then the restricted product `Πʳ i, [R i, A i]` is a locally compact group. - -## Notation - -* `Πʳ i, [R i, A i]_[𝓕]` is `RestrictedProduct R A 𝓕`. -* `Πʳ i, [R i, A i]` is `RestrictedProduct R A cofinite`. - -## Implementation details - -Outside of principal filters and the cofinite filter, the topology we define on the restricted -product does not seem well-behaved. While declaring a single instance is practical, it may conflict -with more interesting topologies in some other cases. Thus, future contributions should not -restrain from specializing these instances to principal and cofinite filters if necessary. - -## Tags - -restricted product, adeles, ideles --/ - -open Set Topology Filter - -variable {ι : Type*} -variable (R : ι → Type*) (A : (i : ι) → Set (R i)) - -/-! -## Definition and elementary maps --/ - -/-- The **restricted product** of a family `R : ι → Type*` of types, relative to subsets -`A : (i : ι) → Set (R i)` and the filter `𝓕 : Filter ι`, is the set of all `x : Π i, R i` -such that the set `{j | x j ∈ A j}` belongs to `𝓕`. We denote it by `Πʳ i, [R i, A i]_[𝓕]`. - -The most common use case is with `𝓕 = cofinite`, in which case the restricted product is the set -of all `x : Π i, R i` such that `x j ∈ A j` for all but finitely many `j`. We denote it simply -by `Πʳ i, [R i, A i]`. - -Similarly, if `S` is a principal filter, the restricted product `Πʳ i, [R i, A i]_[𝓟 s]` -is the set of all `x : Π i, R i` such that `∀ j ∈ S, x j ∈ A j`. -/ -def RestrictedProduct (𝓕 : Filter ι) : Type _ := {x : Π i, R i // ∀ᶠ i in 𝓕, x i ∈ A i} - -open Batteries.ExtendedBinder - -/-- `Πʳ i, [R i, A i]_[𝓕]` is `RestrictedProduct R A 𝓕`. -/ -scoped[RestrictedProduct] -notation3 "Πʳ "(...)", ""["r:(scoped R => R)", "a:(scoped A => A)"]_[" f "]" => - RestrictedProduct r a f - -/-- `Πʳ i, [R i, A i]` is `RestrictedProduct R A cofinite`. -/ -scoped[RestrictedProduct] -notation3"Πʳ "(...)", ""["r:(scoped R => R)", "a:(scoped A => A)"]" => - RestrictedProduct r a cofinite - -namespace RestrictedProduct - -open scoped RestrictedProduct - -variable {𝓕 𝓖 : Filter ι} - -instance : DFunLike (Πʳ i, [R i, A i]_[𝓕]) ι R where - coe x i := x.1 i - coe_injective' _ _ := Subtype.ext - -@[ext] -lemma ext {x y : Πʳ i, [R i, A i]_[𝓕]} (h : ∀ i, x i = y i) : x = y := - Subtype.ext <| funext h - -lemma range_coe : - range ((↑) : Πʳ i, [R i, A i]_[𝓕] → Π i, R i) = {x | ∀ᶠ i in 𝓕, x i ∈ A i} := - Subtype.range_val_subtype - -lemma range_coe_principal {S : Set ι} : - range ((↑) : Πʳ i, [R i, A i]_[𝓟 S] → Π i, R i) = S.pi A := - range_coe R A - -@[simp] lemma eventually (x : Πʳ i, [R i, A i]_[𝓕]) : ∀ᶠ i in 𝓕, x i ∈ A i := x.2 - -variable (𝓕) in -/-- The *structure map* of the restricted product is the obvious inclusion from `Π i, A i` -into `Πʳ i, [R i, A i]_[𝓕]`. -/ -def structureMap (x : Π i, A i) : Πʳ i, [R i, A i]_[𝓕] := - ⟨fun i ↦ x i, .of_forall fun i ↦ (x i).2⟩ - -/-- If `𝓕 ≤ 𝓖`, the restricted product `Πʳ i, [R i, A i]_[𝓖]` is naturally included in -`Πʳ i, [R i, A i]_[𝓕]`. This is the corresponding map. -/ -def inclusion (h : 𝓕 ≤ 𝓖) (x : Πʳ i, [R i, A i]_[𝓖]) : - Πʳ i, [R i, A i]_[𝓕] := - ⟨x, x.2.filter_mono h⟩ - -variable (𝓕) in -lemma inclusion_eq_id : inclusion R A (le_refl 𝓕) = id := rfl - -lemma exists_inclusion_eq_of_eventually (h : 𝓕 ≤ 𝓖) {x : Πʳ i, [R i, A i]_[𝓕]} - (hx𝓖 : ∀ᶠ i in 𝓖, x i ∈ A i) : - ∃ x' : Πʳ i, [R i, A i]_[𝓖], inclusion R A h x' = x := - ⟨⟨x.1, hx𝓖⟩, rfl⟩ - -lemma exists_structureMap_eq_of_forall {x : Πʳ i, [R i, A i]_[𝓕]} - (hx : ∀ i, x.1 i ∈ A i) : - ∃ x' : Π i, A i, structureMap R A 𝓕 x' = x := - ⟨fun i ↦ ⟨x i, hx i⟩, rfl⟩ - -lemma range_inclusion (h : 𝓕 ≤ 𝓖) : - Set.range (inclusion R A h) = {x | ∀ᶠ i in 𝓖, x i ∈ A i} := - subset_antisymm (range_subset_iff.mpr fun x ↦ x.2) - (fun _ hx ↦ mem_range.mpr <| exists_inclusion_eq_of_eventually R A h hx) - -lemma range_structureMap : - Set.range (structureMap R A 𝓕) = {f | ∀ i, f.1 i ∈ A i} := - subset_antisymm (range_subset_iff.mpr fun x i ↦ (x i).2) - (fun _ hx ↦ mem_range.mpr <| exists_structureMap_eq_of_forall R A hx) - -section Algebra -/-! -## Algebraic instances on restricted products - -In this section, we endow the restricted product with its algebraic instances. -To avoid any unnecessary coercions, we use subobject classes for the subset `B i` of each `R i`. --/ - -variable {S : ι → Type*} -- subobject type -variable [Π i, SetLike (S i) (R i)] -variable {B : Π i, S i} - -@[to_additive] -instance [Π i, One (R i)] [∀ i, OneMemClass (S i) (R i)] : One (Πʳ i, [R i, B i]_[𝓕]) where - one := ⟨fun _ ↦ 1, .of_forall fun _ ↦ one_mem _⟩ - -@[to_additive (attr := simp)] -lemma one_apply [Π i, One (R i)] [∀ i, OneMemClass (S i) (R i)] (i : ι) : - (1 : Πʳ i, [R i, B i]_[𝓕]) i = 1 := - rfl - -@[to_additive] -instance [Π i, Inv (R i)] [∀ i, InvMemClass (S i) (R i)] : Inv (Πʳ i, [R i, B i]_[𝓕]) where - inv x := ⟨fun i ↦ (x i)⁻¹, x.2.mono fun _ ↦ inv_mem⟩ - -@[to_additive (attr := simp)] -lemma inv_apply [Π i, Inv (R i)] [∀ i, InvMemClass (S i) (R i)] - (x : Πʳ i, [R i, B i]_[𝓕]) (i : ι) : (x⁻¹) i = (x i)⁻¹ := - rfl - -@[to_additive] -instance [Π i, Mul (R i)] [∀ i, MulMemClass (S i) (R i)] : Mul (Πʳ i, [R i, B i]_[𝓕]) where - mul x y := ⟨fun i ↦ x i * y i, y.2.mp (x.2.mono fun _ ↦ mul_mem)⟩ - -@[to_additive (attr := simp)] -lemma mul_apply [Π i, Mul (R i)] [∀ i, MulMemClass (S i) (R i)] - (x y : Πʳ i, [R i, B i]_[𝓕]) (i : ι) : (x * y) i = x i * y i := - rfl - -@[to_additive] -instance {G : Type*} [Π i, SMul G (R i)] [∀ i, SMulMemClass (S i) G (R i)] : - SMul G (Πʳ i, [R i, B i]_[𝓕]) where - smul g x := ⟨fun i ↦ g • (x i), x.2.mono fun _ ↦ SMulMemClass.smul_mem g⟩ - -@[to_additive (attr := simp)] -lemma smul_apply {G : Type*} [Π i, SMul G (R i)] [∀ i, SMulMemClass (S i) G (R i)] (g : G) - (x : Πʳ i, [R i, B i]_[𝓕]) (i : ι) : (g • x) i = g • x i := - rfl - -@[to_additive] -instance [Π i, DivInvMonoid (R i)] [∀ i, SubgroupClass (S i) (R i)] : - Div (Πʳ i, [R i, B i]_[𝓕]) where - div x y := ⟨fun i ↦ x i / y i, y.2.mp (x.2.mono fun _ ↦ div_mem)⟩ - -@[to_additive (attr := simp)] -lemma div_apply [Π i, DivInvMonoid (R i)] [∀ i, SubgroupClass (S i) (R i)] - (x y : Πʳ i, [R i, B i]_[𝓕]) (i : ι) : (x / y) i = x i / y i := - rfl - -instance [Π i, Monoid (R i)] [∀ i, SubmonoidClass (S i) (R i)] : - Pow (Πʳ i, [R i, B i]_[𝓕]) ℕ where - pow x n := ⟨fun i ↦ x i ^ n, x.2.mono fun _ hi ↦ pow_mem hi n⟩ - -lemma pow_apply [Π i, Monoid (R i)] [∀ i, SubmonoidClass (S i) (R i)] - (x : Πʳ i, [R i, B i]_[𝓕]) (n : ℕ) (i : ι) : (x ^ n) i = x i ^ n := - rfl - -instance [Π i, AddMonoid (R i)] [∀ i, AddSubmonoidClass (S i) (R i)] : - AddMonoid (Πʳ i, [R i, B i]_[𝓕]) := - haveI : ∀ i, SMulMemClass (S i) ℕ (R i) := fun _ ↦ AddSubmonoidClass.nsmulMemClass - DFunLike.coe_injective.addMonoid _ rfl (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) - -@[to_additive existing] -instance [Π i, Monoid (R i)] [∀ i, SubmonoidClass (S i) (R i)] : - Monoid (Πʳ i, [R i, B i]_[𝓕]) := - DFunLike.coe_injective.monoid _ rfl (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) - -instance [Π i, DivInvMonoid (R i)] [∀ i, SubgroupClass (S i) (R i)] : - Pow (Πʳ i, [R i, B i]_[𝓕]) ℤ where - pow x n := ⟨fun i ↦ x i ^ n, x.2.mono fun _ hi ↦ zpow_mem hi n⟩ - -lemma zpow_apply [Π i, DivInvMonoid (R i)] [∀ i, SubgroupClass (S i) (R i)] - (x : Πʳ i, [R i, B i]_[𝓕]) (n : ℤ) (i : ι) : (x ^ n) i = x i ^ n := - rfl - -instance [Π i, AddMonoidWithOne (R i)] [∀ i, AddSubmonoidWithOneClass (S i) (R i)] : - NatCast (Πʳ i, [R i, B i]_[𝓕]) where - natCast n := ⟨fun _ ↦ n, .of_forall fun _ ↦ natCast_mem _ n⟩ - -instance [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] : - IntCast (Πʳ i, [R i, B i]_[𝓕]) where - intCast n := ⟨fun _ ↦ n, .of_forall fun _ ↦ intCast_mem _ n⟩ - -instance [Π i, AddGroup (R i)] [∀ i, AddSubgroupClass (S i) (R i)] : - AddGroup (Πʳ i, [R i, B i]_[𝓕]) := - haveI : ∀ i, SMulMemClass (S i) ℤ (R i) := fun _ ↦ AddSubgroupClass.zsmulMemClass - haveI : ∀ i, SMulMemClass (S i) ℕ (R i) := fun _ ↦ AddSubmonoidClass.nsmulMemClass - DFunLike.coe_injective.addGroup _ rfl (fun _ _ ↦ rfl) (fun _ ↦ rfl) (fun _ _ ↦ rfl) - (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) - -@[to_additive existing] -instance [Π i, Group (R i)] [∀ i, SubgroupClass (S i) (R i)] : - Group (Πʳ i, [R i, B i]_[𝓕]) := - DFunLike.coe_injective.group _ rfl (fun _ _ ↦ rfl) (fun _ ↦ rfl) (fun _ _ ↦ rfl) - (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) - -instance [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] : - Ring (Πʳ i, [R i, B i]_[𝓕]) := - DFunLike.coe_injective.ring _ rfl rfl (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) (fun _ ↦ rfl) - (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) (fun _ ↦ rfl) (fun _ ↦ rfl) - -instance [Π i, CommRing (R i)] [∀ i, SubringClass (S i) (R i)] : - CommRing (Πʳ i, [R i, B i]_[𝓕]) where - mul_comm _ _ := DFunLike.coe_injective <| funext (fun _ ↦ mul_comm _ _) - -end Algebra - -section eval - -variable {S : ι → Type*} -variable [Π i, SetLike (S i) (R i)] -variable {B : Π i, S i} - -/-- `RestrictedProduct.evalMonoidHom j` is the monoid homomorphism from the restricted -product `Πʳ i, [R i, B i]_[𝓕]` to the component `R j`. --/ -@[to_additive "`RestrictedProduct.evalAddMonoidHom j` is the monoid homomorphism from the restricted -product `Πʳ i, [R i, B i]_[𝓕]` to the component `R j`."] -def evalMonoidHom (j : ι) [Π i, Monoid (R i)] [∀ i, SubmonoidClass (S i) (R i)] : - (Πʳ i, [R i, B i]_[𝓕]) →* R j where - toFun x := x j - map_one' := rfl - map_mul' _ _ := rfl - -@[simp] -lemma evalMonoidHom_apply [Π i, Monoid (R i)] [∀ i, SubmonoidClass (S i) (R i)] - (x : Πʳ i, [R i, B i]_[𝓕]) (j : ι) : evalMonoidHom R j x = x j := - rfl - -/-- `RestrictedProduct.evalRingHom j` is the ring homomorphism from the restricted -product `Πʳ i, [R i, B i]_[𝓕]` to the component `R j`. --/ -def evalRingHom (j : ι) [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] : - (Πʳ i, [R i, B i]_[𝓕]) →+* R j where - __ := evalMonoidHom R j - __ := evalAddMonoidHom R j - -@[simp] -lemma evalRingHom_apply [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] - (x : Πʳ i, [R i, B i]_[𝓕]) (j : ι) : evalRingHom R j x = x j := - rfl - -end eval - -section map - -variable {ι₁ ι₂ : Type*} -variable (R₁ : ι₁ → Type*) (R₂ : ι₂ → Type*) -variable {𝓕₁ : Filter ι₁} {𝓕₂ : Filter ι₂} -variable {A₁ : (i : ι₁) → Set (R₁ i)} {A₂ : (i : ι₂) → Set (R₂ i)} -variable {S₁ : ι₁ → Type*} {S₂ : ι₂ → Type*} -variable [Π i, SetLike (S₁ i) (R₁ i)] [Π j, SetLike (S₂ j) (R₂ j)] -variable {B₁ : Π i, S₁ i} {B₂ : Π j, S₂ j} -variable (f : ι₂ → ι₁) (hf : Tendsto f 𝓕₂ 𝓕₁) - -section set - -variable (φ : ∀ j, R₁ (f j) → R₂ j) (hφ : ∀ᶠ j in 𝓕₂, MapsTo (φ j) (A₁ (f j)) (A₂ j)) - -/-- -Given two restricted products `Πʳ (i : ι₁), [R₁ i, A₁ i]_[𝓕₁]` and `Πʳ (j : ι₂), [R₂ j, A₂ j]_[𝓕₂]`, -`RestrictedProduct.map` gives a function between them. The data needed is a function `f : ι₂ → ι₁` -such that `𝓕₂` tends to `𝓕₁` along `f`, and functions `φ j : R₁ (f j) → R₂ j` -sending `A₁ (f j)` into `A₂ j` for an `𝓕₂`-large set of `j`'s. - -See also `mapMonoidHom`, `mapAddMonoidHom` and `mapRingHom` for variants. --/ -def map (x : Πʳ i, [R₁ i, A₁ i]_[𝓕₁]) : Πʳ j, [R₂ j, A₂ j]_[𝓕₂] := ⟨fun j ↦ φ j (x (f j)), by - filter_upwards [hf.eventually x.2, hφ] using fun _ h1 h2 ↦ h2 h1⟩ - -@[simp] -lemma map_apply (x : Πʳ i, [R₁ i, A₁ i]_[𝓕₁]) (j : ι₂) : - x.map R₁ R₂ f hf φ hφ j = φ j (x (f j)) := - rfl - -end set - -section monoid - -variable [Π i, Monoid (R₁ i)] [Π i, Monoid (R₂ i)] [∀ i, SubmonoidClass (S₁ i) (R₁ i)] - [∀ i, SubmonoidClass (S₂ i) (R₂ i)] (φ : ∀ j, R₁ (f j) →* R₂ j) - (hφ : ∀ᶠ j in 𝓕₂, MapsTo (φ j) (B₁ (f j)) (B₂ j)) - -/-- -Given two restricted products `Πʳ (i : ι₁), [R₁ i, B₁ i]_[𝓕₁]` and `Πʳ (j : ι₂), [R₂ j, B₂ j]_[𝓕₂]`, -`RestrictedProduct.mapMonoidHom` gives a monoid homomorphism between them. The data needed is a -function `f : ι₂ → ι₁` such that `𝓕₂` tends to `𝓕₁` along `f`, and monoid homomorphisms -`φ j : R₁ (f j) → R₂ j` sending `B₁ (f j)` into `B₂ j` for an `𝓕₂`-large set of `j`'s. --/ -@[to_additive " -Given two restricted products `Πʳ (i : ι₁), [R₁ i, B₁ i]_[𝓕₁]` and `Πʳ (j : ι₂), [R₂ j, B₂ j]_[𝓕₂]`, -`RestrictedProduct.mapAddMonoidHom` gives a additive monoid homomorphism between them. The data -needed is a function `f : ι₂ → ι₁` such that `𝓕₂` tends to `𝓕₁` along `f`, and -additive monoid homomorphisms `φ j : R₁ (f j) → R₂ j` sending `B₁ (f j)` into `B₂ j` for -an `𝓕₂`-large set of `j`'s. -"] -def mapMonoidHom : Πʳ i, [R₁ i, B₁ i]_[𝓕₁] →* Πʳ j, [R₂ j, B₂ j]_[𝓕₂] where - toFun := map R₁ R₂ f hf (fun j r ↦ φ j r) hφ - map_one' := by - ext i - exact map_one (φ i) - map_mul' x y := by - ext i - exact map_mul (φ i) _ _ - -@[to_additive (attr := simp)] -lemma mapMonoidHom_apply (x : Πʳ i, [R₁ i, B₁ i]_[𝓕₁]) (j : ι₂) : - x.mapMonoidHom R₁ R₂ f hf φ hφ j = φ j (x (f j)) := - rfl - -end monoid - -section ring - -variable [Π i, Ring (R₁ i)] [Π i, Ring (R₂ i)] [∀ i, SubringClass (S₁ i) (R₁ i)] - [∀ i, SubringClass (S₂ i) (R₂ i)] (φ : ∀ j, R₁ (f j) →+* R₂ j) - (hφ : ∀ᶠ j in 𝓕₂, MapsTo (φ j) (B₁ (f j)) (B₂ j)) - -/-- -Given two restricted products `Πʳ (i : ι₁), [R₁ i, B₁ i]_[𝓕₁]` and `Πʳ (j : ι₂), [R₂ j, B₂ j]_[𝓕₂]`, -`RestrictedProduct.mapRingHom` gives a ring homomorphism between them. The data needed is a -function `f : ι₂ → ι₁` such that `𝓕₂` tends to `𝓕₁` along `f`, and ring homomorphisms -`φ j : R₁ (f j) → R₂ j` sending `B₁ (f j)` into `B₂ j` for an `𝓕₂`-large set of `j`'s. --/ -def mapRingHom : Πʳ i, [R₁ i, B₁ i]_[𝓕₁] →+* Πʳ j, [R₂ j, B₂ j]_[𝓕₂] where - __ := mapMonoidHom R₁ R₂ f hf (fun j ↦ φ j) hφ - __ := mapAddMonoidHom R₁ R₂ f hf (fun j ↦ φ j) hφ - -@[simp] -lemma mapRingHom_apply (x : Πʳ i, [R₁ i, B₁ i]_[𝓕₁]) (j : ι₂) : - x.mapRingHom R₁ R₂ f hf φ hφ j = φ j (x (f j)) := - rfl - -end ring - -end map - -section Topology -/-! -## Topology on the restricted product - -The topology on the restricted product `Πʳ i, [R i, A i]_[𝓕]` is defined in the following way: -1. If `𝓕` is some principal filter `𝓟 s`, recall that `Πʳ i, [R i, A i]_[𝓟 s]` is canonically -identified with `(Π i ∈ s, A i) × (Π i ∉ s, R i)`. We endow it with the product topology, -which is also the topology induced from the full product `Π i, R i`. -2. In general, we note that `𝓕` is the infimum of the principal filters coarser than `𝓕`. We -then endow `Πʳ i, [R i, A i]_[𝓕]` with the inductive limit / final topology associated to the -inclusion maps `Πʳ i, [R i, A i]_[𝓟 s] → Πʳ i, [R i, A i]_[𝓕]` where `𝓕 ≤ 𝓟 s`. - -In particular: -* On the classical restricted product, with respect to the cofinite filter, this corresponds to - taking the inductive limit of the `Πʳ i, [R i, A i]_[𝓟 s]` over all *cofinite* sets `s : Set ι`. -* If `𝓕 = 𝓟 s` is a principal filter, this second step clearly does not change the topology, since - `s` belongs to the indexing set of the inductive limit. - -Taking advantage of that second remark, we do not actually declare an instance specific to -principal filters. Instead, we provide directly the general instance (corresponding to step 2 above) -as `RestrictedProduct.topologicalSpace`. We then prove that, for a principal filter, the -map to the full product is an inducing (`RestrictedProduct.isEmbedding_coe_of_principal`), -and that the topology for a general `𝓕` is indeed the expected inductive limit -(`RestrictedProduct.topologicalSpace_eq_iSup`). - -Note: outside of these two cases, this topology on the restricted product does not seem -well-behaved. While declaring a single instance is practical, it may conflict with more interesting -topologies in some other cases. Thus, future contributions should not restrain from specializing -these instances to principal and cofinite filters if necessary. --/ - -/-! -### Definition of the topology --/ - -variable {R A R' A'} -variable {𝓕 : Filter ι} -variable [∀ i, TopologicalSpace (R i)] - -variable (R A 𝓕) in -instance topologicalSpace : TopologicalSpace (Πʳ i, [R i, A i]_[𝓕]) := - ⨆ (S : Set ι) (hS : 𝓕 ≤ 𝓟 S), .coinduced (inclusion R A hS) - (.induced ((↑) : Πʳ i, [R i, A i]_[𝓟 S] → Π i, R i) inferInstance) - -@[fun_prop] -theorem continuous_coe : - Continuous ((↑) : Πʳ i, [R i, A i]_[𝓕] → Π i, R i) := - continuous_iSup_dom.mpr fun _ ↦ continuous_iSup_dom.mpr fun _ ↦ - continuous_coinduced_dom.mpr continuous_induced_dom - -@[fun_prop] -theorem continuous_eval (i : ι) : - Continuous (fun (x : Πʳ i, [R i, A i]_[𝓕]) ↦ x i) := - continuous_apply _ |>.comp continuous_coe - -@[fun_prop] -theorem continuous_inclusion {𝓖 : Filter ι} (h : 𝓕 ≤ 𝓖) : - Continuous (inclusion R A h) := by - simp_rw [continuous_iff_coinduced_le, topologicalSpace, coinduced_iSup, coinduced_compose] - exact iSup₂_le fun S hS ↦ le_iSup₂_of_le S (le_trans h hS) le_rfl - -instance [∀ i, T0Space (R i)] : T0Space (Πʳ i, [R i, A i]_[𝓕]) := - t0Space_of_injective_of_continuous DFunLike.coe_injective continuous_coe - -instance [∀ i, T1Space (R i)] : T1Space (Πʳ i, [R i, A i]_[𝓕]) := - t1Space_of_injective_of_continuous DFunLike.coe_injective continuous_coe - -instance [∀ i, T2Space (R i)] : T2Space (Πʳ i, [R i, A i]_[𝓕]) := - .of_injective_continuous DFunLike.coe_injective continuous_coe - -section principal -/-! -### Topological facts in the principal case --/ - -variable {S : Set ι} - -theorem topologicalSpace_eq_of_principal : - topologicalSpace R A (𝓟 S) = - .induced ((↑) : Πʳ i, [R i, A i]_[𝓟 S] → Π i, R i) inferInstance := - le_antisymm (continuous_iff_le_induced.mp continuous_coe) <| - (le_iSup₂_of_le S le_rfl <| by rw [inclusion_eq_id R A (𝓟 S), @coinduced_id]) - -theorem topologicalSpace_eq_of_top : - topologicalSpace R A ⊤ = - .induced ((↑) : Πʳ i, [R i, A i]_[⊤] → Π i, R i) inferInstance := - principal_univ ▸ topologicalSpace_eq_of_principal - -theorem topologicalSpace_eq_of_bot : - topologicalSpace R A ⊥ = - .induced ((↑) : Πʳ i, [R i, A i]_[⊥] → Π i, R i) inferInstance := - principal_empty ▸ topologicalSpace_eq_of_principal - -theorem isEmbedding_coe_of_principal : - IsEmbedding ((↑) : Πʳ i, [R i, A i]_[𝓟 S] → Π i, R i) where - eq_induced := topologicalSpace_eq_of_principal - injective := DFunLike.coe_injective - -theorem isEmbedding_coe_of_top : - IsEmbedding ((↑) : Πʳ i, [R i, A i]_[⊤] → Π i, R i) := - principal_univ ▸ isEmbedding_coe_of_principal - -theorem isEmbedding_coe_of_bot : - IsEmbedding ((↑) : Πʳ i, [R i, A i]_[⊥] → Π i, R i) := - principal_empty ▸ isEmbedding_coe_of_principal - -theorem continuous_rng_of_principal {X : Type*} [TopologicalSpace X] - {f : X → Πʳ i, [R i, A i]_[𝓟 S]} : - Continuous f ↔ Continuous ((↑) ∘ f : X → Π i, R i) := - isEmbedding_coe_of_principal.continuous_iff - -theorem continuous_rng_of_top {X : Type*} [TopologicalSpace X] - {f : X → Πʳ i, [R i, A i]_[⊤]} : - Continuous f ↔ Continuous ((↑) ∘ f : X → Π i, R i) := - isEmbedding_coe_of_top.continuous_iff - -theorem continuous_rng_of_bot {X : Type*} [TopologicalSpace X] - {f : X → Πʳ i, [R i, A i]_[⊥]} : - Continuous f ↔ Continuous ((↑) ∘ f : X → Π i, R i) := - isEmbedding_coe_of_bot.continuous_iff - -lemma continuous_rng_of_principal_iff_forall {X : Type*} [TopologicalSpace X] - {f : X → Πʳ (i : ι), [R i, A i]_[𝓟 S]} : - Continuous f ↔ ∀ i : ι, Continuous ((fun x ↦ x i) ∘ f) := - continuous_rng_of_principal.trans continuous_pi_iff - -/-- The obvious bijection between `Πʳ i, [R i, A i]_[⊤]` and `Π i, A i` is a homeomorphism. -/ -def homeoTop : (Π i, A i) ≃ₜ (Πʳ i, [R i, A i]_[⊤]) where - toFun f := ⟨fun i ↦ f i, fun i ↦ (f i).2⟩ - invFun f i := ⟨f i, f.2 i⟩ - continuous_toFun := continuous_rng_of_top.mpr <| continuous_pi fun i ↦ - continuous_subtype_val.comp <| continuous_apply i - continuous_invFun := continuous_pi fun i ↦ continuous_induced_rng.mpr <| continuous_eval i - left_inv _ := rfl - right_inv _ := rfl - -/-- The obvious bijection between `Πʳ i, [R i, A i]_[⊥]` and `Π i, R i` is a homeomorphism. -/ -def homeoBot : (Π i, R i) ≃ₜ (Πʳ i, [R i, A i]_[⊥]) where - toFun f := ⟨fun i ↦ f i, eventually_bot⟩ - invFun f i := f i - continuous_toFun := continuous_rng_of_bot.mpr <| continuous_pi fun i ↦ continuous_apply i - continuous_invFun := continuous_pi continuous_eval - left_inv _ := rfl - right_inv _ := rfl - -/-- Assume that `S` is a subset of `ι` with finite complement, that each `R i` is weakly locally -compact, and that `A i` is *compact* for all `i ∈ S`. Then the restricted product -`Πʳ i, [R i, A i]_[𝓟 S]` is locally compact. - -Note: we spell "`S` has finite complement" as `cofinite ≤ 𝓟 S`. -/ -theorem weaklyLocallyCompactSpace_of_principal [∀ i, WeaklyLocallyCompactSpace (R i)] - (hS : cofinite ≤ 𝓟 S) (hAcompact : ∀ i ∈ S, IsCompact (A i)) : - WeaklyLocallyCompactSpace (Πʳ i, [R i, A i]_[𝓟 S]) where - exists_compact_mem_nhds := fun x ↦ by - rw [le_principal_iff, mem_cofinite] at hS - classical - have : ∀ i, ∃ K, IsCompact K ∧ K ∈ 𝓝 (x i) := fun i ↦ exists_compact_mem_nhds (x i) - choose K K_compact hK using this - set Q : Set (Π i, R i) := univ.pi (fun i ↦ if i ∈ S then A i else K i) with Q_def - have Q_compact : IsCompact Q := isCompact_univ_pi fun i ↦ by - split_ifs with his - · exact hAcompact i his - · exact K_compact i - set U : Set (Π i, R i) := Sᶜ.pi K - have U_nhds : U ∈ 𝓝 (x : Π i, R i) := set_pi_mem_nhds hS fun i _ ↦ hK i - have QU : (↑) ⁻¹' U ⊆ ((↑) ⁻¹' Q : Set (Πʳ i, [R i, A i]_[𝓟 S])) := fun y H i _ ↦ by - dsimp only - split_ifs with hi - · exact y.2 hi - · exact H i hi - refine ⟨((↑) ⁻¹' Q), ?_, mem_of_superset ?_ QU⟩ - · refine isEmbedding_coe_of_principal.isCompact_preimage_iff ?_ |>.mpr Q_compact - simp_rw [range_coe_principal, Q_def, pi_if, mem_univ, true_and] - exact inter_subset_left - · simpa only [isEmbedding_coe_of_principal.nhds_eq_comap] using preimage_mem_comap U_nhds - -instance [∀ i, WeaklyLocallyCompactSpace (R i)] [hS : Fact (cofinite ≤ 𝓟 S)] - [hAcompact : ∀ i, CompactSpace (A i)] : - WeaklyLocallyCompactSpace (Πʳ i, [R i, A i]_[𝓟 S]) := - weaklyLocallyCompactSpace_of_principal hS.out - fun _ _ ↦ isCompact_iff_compactSpace.mpr inferInstance - -end principal - -section general -/-! -### Topological facts in the general case --/ - -variable (𝓕) in -theorem topologicalSpace_eq_iSup : - topologicalSpace R A 𝓕 = ⨆ (S : Set ι) (hS : 𝓕 ≤ 𝓟 S), - .coinduced (inclusion R A hS) (topologicalSpace R A (𝓟 S)) := by - simp_rw [topologicalSpace_eq_of_principal, topologicalSpace] - -/-- The **universal property** of the topology on the restricted product: a map from -`Πʳ i, [R i, A i]_[𝓕]` is continuous *iff* its restriction to each `Πʳ i, [R i, A i]_[𝓟 s]` -(with `𝓕 ≤ 𝓟 s`) is continuous. - -See also `RestrictedProduct.continuous_dom_prod_left`. -/ -theorem continuous_dom {X : Type*} [TopologicalSpace X] - {f : Πʳ i, [R i, A i]_[𝓕] → X} : - Continuous f ↔ ∀ (S : Set ι) (hS : 𝓕 ≤ 𝓟 S), Continuous (f ∘ inclusion R A hS) := by - simp_rw [topologicalSpace_eq_of_principal, continuous_iSup_dom, continuous_coinduced_dom] - -theorem isEmbedding_inclusion_principal {S : Set ι} (hS : 𝓕 ≤ 𝓟 S) : - IsEmbedding (inclusion R A hS) := - .of_comp (continuous_inclusion hS) continuous_coe isEmbedding_coe_of_principal - -theorem isEmbedding_inclusion_top : - IsEmbedding (inclusion R A (le_top : 𝓕 ≤ ⊤)) := - .of_comp (continuous_inclusion _) continuous_coe isEmbedding_coe_of_top - -/-- `Π i, A i` has the subset topology from the restricted product. -/ -theorem isEmbedding_structureMap : - IsEmbedding (structureMap R A 𝓕) := - isEmbedding_inclusion_top.comp homeoTop.isEmbedding - -end general - -section cofinite -/-! -### Topological facts in the case where `𝓕 = cofinite` and all `A i`s are open - -The classical restricted product, associated to the cofinite filter, satisfies more topological -properties when each `A i` is an open subset of `R i`. The key fact is that each -`Πʳ i, [R i, A i]_[𝓟 S]` (with `S` cofinite) then embeds **as an open subset** in -`Πʳ i, [R i, A i]`. - -This allows us to prove a "universal property with parameters", expressing that for any -arbitrary topolgical space `X` (of "parameters"), the product `X × Πʳ i, [R i, A i]` -is still the inductive limit of the `X × Πʳ i, [R i, A i]_[𝓟 S]` for `S` cofinite. - -This fact, which is **not true** for a general inductive limit, will allow us to prove continuity -of functions of two variables (e.g algebraic operations), which would otherwise be inaccessible. --/ - -variable (hAopen : ∀ i, IsOpen (A i)) - -include hAopen in -theorem isOpen_forall_imp_mem_of_principal {S : Set ι} (hS : cofinite ≤ 𝓟 S) {p : ι → Prop} : - IsOpen {f : Πʳ i, [R i, A i]_[𝓟 S] | ∀ i, p i → f.1 i ∈ A i} := by - rw [le_principal_iff] at hS - convert isOpen_set_pi (hS.inter_of_left {i | p i}) (fun i _ ↦ hAopen i) |>.preimage continuous_coe - ext f - refine ⟨fun H i hi ↦ H i hi.2, fun H i hiT ↦ ?_⟩ - by_cases hiS : i ∈ S - · exact f.2 hiS - · exact H i ⟨hiS, hiT⟩ - -include hAopen in -theorem isOpen_forall_mem_of_principal {S : Set ι} (hS : cofinite ≤ 𝓟 S) : - IsOpen {f : Πʳ i, [R i, A i]_[𝓟 S] | ∀ i, f.1 i ∈ A i} := by - convert isOpen_forall_imp_mem_of_principal hAopen hS (p := fun _ ↦ True) - simp - -include hAopen in -theorem isOpen_forall_imp_mem {p : ι → Prop} : - IsOpen {f : Πʳ i, [R i, A i] | ∀ i, p i → f.1 i ∈ A i} := by - simp_rw [topologicalSpace_eq_iSup cofinite, isOpen_iSup_iff, isOpen_coinduced] - exact fun S hS ↦ isOpen_forall_imp_mem_of_principal hAopen hS - -include hAopen in -theorem isOpen_forall_mem : - IsOpen {f : Πʳ i, [R i, A i] | ∀ i, f.1 i ∈ A i} := by - simp_rw [topologicalSpace_eq_iSup cofinite, isOpen_iSup_iff, isOpen_coinduced] - exact fun S hS ↦ isOpen_forall_mem_of_principal hAopen hS - -include hAopen in -theorem isOpenEmbedding_inclusion_principal {S : Set ι} (hS : cofinite ≤ 𝓟 S) : - IsOpenEmbedding (inclusion R A hS) where - toIsEmbedding := isEmbedding_inclusion_principal hS - isOpen_range := by - rw [range_inclusion] - exact isOpen_forall_imp_mem hAopen - -include hAopen in -/-- `Π i, A i` is homeomorphic to an open subset of the restricted product. -/ -theorem isOpenEmbedding_structureMap : - IsOpenEmbedding (structureMap R A cofinite) where - toIsEmbedding := isEmbedding_structureMap - isOpen_range := by - rw [range_structureMap] - exact isOpen_forall_mem hAopen - -include hAopen in -theorem nhds_eq_map_inclusion {S : Set ι} (hS : cofinite ≤ 𝓟 S) - (x : Πʳ i, [R i, A i]_[𝓟 S]) : - (𝓝 (inclusion R A hS x)) = .map (inclusion R A hS) (𝓝 x) := by - rw [isOpenEmbedding_inclusion_principal hAopen hS |>.map_nhds_eq x] - -include hAopen in -theorem nhds_eq_map_structureMap - (x : Π i, A i) : - (𝓝 (structureMap R A cofinite x)) = .map (structureMap R A cofinite) (𝓝 x) := by - rw [isOpenEmbedding_structureMap hAopen |>.map_nhds_eq x] - -include hAopen in -/-- If each `R i` is weakly locally compact, each `A i` is open, and all but finitely many `A i`s -are also compact, then the restricted product `Πʳ i, [R i, A i]` is weakly locally compact. -/ -theorem weaklyLocallyCompactSpace_of_cofinite [∀ i, WeaklyLocallyCompactSpace (R i)] - (hAcompact : ∀ᶠ i in cofinite, IsCompact (A i)) : - WeaklyLocallyCompactSpace (Πʳ i, [R i, A i]) where - exists_compact_mem_nhds := fun x ↦ by - set S := {i | IsCompact (A i) ∧ x i ∈ A i} - have hS : cofinite ≤ 𝓟 S := le_principal_iff.mpr (hAcompact.and x.2) - have hSx : ∀ i ∈ S, x i ∈ A i := fun i hi ↦ hi.2 - have hSA : ∀ i ∈ S, IsCompact (A i) := fun i hi ↦ hi.1 - haveI := weaklyLocallyCompactSpace_of_principal hS hSA - rcases exists_inclusion_eq_of_eventually R A hS hSx with ⟨x', hxx'⟩ - rw [← hxx', nhds_eq_map_inclusion hAopen] - rcases exists_compact_mem_nhds x' with ⟨K, K_compact, hK⟩ - exact ⟨inclusion R A hS '' K, K_compact.image (continuous_inclusion hS), image_mem_map hK⟩ - -instance [hAopen : Fact (∀ i, IsOpen (A i))] [∀ i, WeaklyLocallyCompactSpace (R i)] - [hAcompact : ∀ i, CompactSpace (A i)] : - WeaklyLocallyCompactSpace (Πʳ i, [R i, A i]) := - weaklyLocallyCompactSpace_of_cofinite hAopen.out <| - .of_forall fun _ ↦ isCompact_iff_compactSpace.mpr inferInstance - -include hAopen in -/-- The **universal property with parameters** of the topology on the restricted product: -for any topological space `Y` of "parameters", a map from `(Πʳ i, [R i, A i]) × Y` is continuous -*iff* its restriction to each `(Πʳ i, [R i, A i]_[𝓟 S]) × Y` (with `S` cofinite) is continuous. -/ -theorem continuous_dom_prod_right {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] - {f : Πʳ i, [R i, A i] × Y → X} : - Continuous f ↔ ∀ (S : Set ι) (hS : cofinite ≤ 𝓟 S), - Continuous (f ∘ (Prod.map (inclusion R A hS) id)) := by - refine ⟨fun H S hS ↦ H.comp ((continuous_inclusion hS).prodMap continuous_id), - fun H ↦ ?_⟩ - simp_rw [continuous_iff_continuousAt, ContinuousAt] - rintro ⟨x, y⟩ - set S : Set ι := {i | x i ∈ A i} - have hS : cofinite ≤ 𝓟 S := le_principal_iff.mpr x.2 - have hxS : ∀ i ∈ S, x i ∈ A i := fun i hi ↦ hi - rcases exists_inclusion_eq_of_eventually R A hS hxS with ⟨x', hxx'⟩ - rw [← hxx', nhds_prod_eq, nhds_eq_map_inclusion hAopen hS x', - ← Filter.map_id (f := 𝓝 y), prod_map_map_eq, ← nhds_prod_eq, tendsto_map'_iff] - exact H S hS |>.tendsto ⟨x', y⟩ - --- TODO: get from the previous one instead of copy-pasting -include hAopen in -/-- The **universal property with parameters** of the topology on the restricted product: -for any topological space `Y` of "parameters", a map from `Y × Πʳ i, [R i, A i]` is continuous -*iff* its restriction to each `Y × Πʳ i, [R i, A i]_[𝓟 S]` (with `S` cofinite) is continuous. -/ -theorem continuous_dom_prod_left {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] - {f : Y × Πʳ i, [R i, A i] → X} : - Continuous f ↔ ∀ (S : Set ι) (hS : cofinite ≤ 𝓟 S), - Continuous (f ∘ (Prod.map id (inclusion R A hS))) := by - refine ⟨fun H S hS ↦ H.comp (continuous_id.prodMap (continuous_inclusion hS)), - fun H ↦ ?_⟩ - simp_rw [continuous_iff_continuousAt, ContinuousAt] - rintro ⟨y, x⟩ - set S : Set ι := {i | x i ∈ A i} - have hS : cofinite ≤ 𝓟 S := le_principal_iff.mpr x.2 - have hxS : ∀ i ∈ S, x i ∈ A i := fun i hi ↦ hi - rcases exists_inclusion_eq_of_eventually R A hS hxS with ⟨x', hxx'⟩ - rw [← hxx', nhds_prod_eq, nhds_eq_map_inclusion hAopen hS x', - ← Filter.map_id (f := 𝓝 y), prod_map_map_eq, ← nhds_prod_eq, tendsto_map'_iff] - exact H S hS |>.tendsto ⟨y, x'⟩ - -include hAopen in -/-- A map from `Πʳ i, [R i, A i] × Πʳ i, [R' i, A' i]` is continuous -*iff* its restriction to each `Πʳ i, [R i, A i]_[𝓟 S] × Πʳ i, [R' i, A' i]_[𝓟 S]` -(with `S` cofinite) is continuous. - -This is the key result for continuity of multiplication and addition. -/ -theorem continuous_dom_prod {R' : ι → Type*} {A' : (i : ι) → Set (R' i)} - [∀ i, TopologicalSpace (R' i)] (hAopen' : ∀ i, IsOpen (A' i)) - {X : Type*} [TopologicalSpace X] - {f : Πʳ i, [R i, A i] × Πʳ i, [R' i, A' i] → X} : - Continuous f ↔ ∀ (S : Set ι) (hS : cofinite ≤ 𝓟 S), - Continuous (f ∘ (Prod.map (inclusion R A hS) (inclusion R' A' hS))) := by - simp_rw [continuous_dom_prod_right hAopen, continuous_dom_prod_left hAopen'] - refine ⟨fun H S hS ↦ H S hS S hS, fun H S hS T hT ↦ ?_⟩ - set U := S ∩ T - have hU : cofinite ≤ 𝓟 (S ∩ T) := inf_principal ▸ le_inf hS hT - have hSU : 𝓟 U ≤ 𝓟 S := principal_mono.mpr inter_subset_left - have hTU : 𝓟 U ≤ 𝓟 T := principal_mono.mpr inter_subset_right - exact (H U hU).comp ((continuous_inclusion hSU).prodMap (continuous_inclusion hTU)) - -/-- A finitary (instead of binary) version of `continuous_dom_prod`. -/ -theorem continuous_dom_pi {n : Type*} [Fintype n] {X : Type*} - [TopologicalSpace X] {A : n → ι → Type*} - [∀ j i, TopologicalSpace (A j i)] - {C : (j : n) → (i : ι) → Set (A j i)} - (hCopen : ∀ j i, IsOpen (C j i)) - {f : (Π j : n, Πʳ i : ι, [A j i, C j i]) → X} : - Continuous f ↔ - ∀ (S : Set ι) (hS : cofinite ≤ 𝓟 S), Continuous (f ∘ Pi.map fun _ ↦ inclusion _ _ hS) := by - refine ⟨by fun_prop, fun H ↦ ?_⟩ - simp_rw [continuous_iff_continuousAt, ContinuousAt] - intro x - set S : Set ι := {i | ∀ j, x j i ∈ C j i} - have hS : cofinite ≤ 𝓟 S := by - rw [le_principal_iff] - change ∀ᶠ i in cofinite, ∀ j : n, x j i ∈ C j i - simp [- eventually_cofinite] - let x' (j : n) : Πʳ i : ι, [A j i, C j i]_[𝓟 S] := .mk (fun i ↦ x j i) (fun i hi ↦ hi _) - have hxx' : Pi.map (fun j ↦ inclusion _ _ hS) x' = x := rfl - simp_rw [← hxx', nhds_pi, Pi.map_apply, nhds_eq_map_inclusion (hCopen _), ← map_piMap_pi_finite, - tendsto_map'_iff, ← nhds_pi] - exact (H _ _).tendsto _ - -end cofinite - -end Topology - -section Compatibility -/-! -## Compatibility properties between algebra and topology --/ - -variable {S : ι → Type*} -- subobject type -variable [Π i, SetLike (S i) (R i)] -variable {B : Π i, S i} -variable {T : Set ι} {𝓕 : Filter ι} -variable [Π i, TopologicalSpace (R i)] - -section general - -@[to_additive] -instance [Π i, Inv (R i)] [∀ i, InvMemClass (S i) (R i)] [∀ i, ContinuousInv (R i)] : - ContinuousInv (Πʳ i, [R i, B i]_[𝓕]) where - continuous_inv := by - rw [continuous_dom] - intro T hT - haveI : ContinuousInv (Πʳ i, [R i, B i]_[𝓟 T]) := - isEmbedding_coe_of_principal.continuousInv fun _ ↦ rfl - exact (continuous_inclusion hT).comp continuous_inv - -@[to_additive] -instance {G : Type*} [Π i, SMul G (R i)] [∀ i, SMulMemClass (S i) G (R i)] - [∀ i, ContinuousConstSMul G (R i)] : - ContinuousConstSMul G (Πʳ i, [R i, B i]_[𝓕]) where - continuous_const_smul g := by - rw [continuous_dom] - intro T hT - haveI : ContinuousConstSMul G (Πʳ i, [R i, B i]_[𝓟 T]) := - isEmbedding_coe_of_principal.continuousConstSMul id rfl - exact (continuous_inclusion hT).comp (continuous_const_smul g) - -end general - -section principal - -@[to_additive] -instance [Π i, Mul (R i)] [∀ i, MulMemClass (S i) (R i)] [∀ i, ContinuousMul (R i)] : - ContinuousMul (Πʳ i, [R i, B i]_[𝓟 T]) := - let φ : Πʳ i, [R i, B i]_[𝓟 T] →ₙ* Π i, R i := - { toFun := (↑) - map_mul' := fun _ _ ↦ rfl } - isEmbedding_coe_of_principal.continuousMul φ - -@[to_additive] -instance {G : Type*} [TopologicalSpace G] [Π i, SMul G (R i)] [∀ i, SMulMemClass (S i) G (R i)] - [∀ i, ContinuousSMul G (R i)] : - ContinuousSMul G (Πʳ i, [R i, B i]_[𝓟 T]) := - isEmbedding_coe_of_principal.continuousSMul continuous_id rfl - -@[to_additive] -instance [Π i, Group (R i)] [∀ i, SubgroupClass (S i) (R i)] [∀ i, IsTopologicalGroup (R i)] : - IsTopologicalGroup (Πʳ i, [R i, B i]_[𝓟 T]) where - -instance [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] [∀ i, IsTopologicalRing (R i)] : - IsTopologicalRing (Πʳ i, [R i, B i]_[𝓟 T]) where - -end principal - -section cofinite - -theorem nhds_zero_eq_map_ofPre [Π i, Zero (R i)] [∀ i, ZeroMemClass (S i) (R i)] - (hBopen : ∀ i, IsOpen (B i : Set (R i))) (hT : cofinite ≤ 𝓟 T) : - (𝓝 (inclusion R (fun i ↦ B i) hT 0)) = .map (inclusion R (fun i ↦ B i) hT) (𝓝 0) := - nhds_eq_map_inclusion hBopen hT 0 - -theorem nhds_zero_eq_map_structureMap [Π i, Zero (R i)] [∀ i, ZeroMemClass (S i) (R i)] - (hBopen : ∀ i, IsOpen (B i : Set (R i))) : - (𝓝 (structureMap R (fun i ↦ B i) cofinite 0)) = - .map (structureMap R (fun i ↦ B i) cofinite) (𝓝 0) := - nhds_eq_map_structureMap hBopen 0 - --- TODO: Make `IsOpen` a class like `IsClosed` ? -variable [hBopen : Fact (∀ i, IsOpen (B i : Set (R i)))] - -@[to_additive] -instance [Π i, Mul (R i)] [∀ i, MulMemClass (S i) (R i)] [∀ i, ContinuousMul (R i)] : - ContinuousMul (Πʳ i, [R i, B i]) where - continuous_mul := by - rw [continuous_dom_prod hBopen.out hBopen.out] - exact fun S hS ↦ (continuous_inclusion hS).comp continuous_mul - -@[to_additive] -instance continuousSMul {G : Type*} [TopologicalSpace G] [Π i, SMul G (R i)] - [∀ i, SMulMemClass (S i) G (R i)] [∀ i, ContinuousSMul G (R i)] : - ContinuousSMul G (Πʳ i, [R i, B i]) where - continuous_smul := by - rw [continuous_dom_prod_left hBopen.out] - exact fun S hS ↦ (continuous_inclusion hS).comp continuous_smul - -@[to_additive] -instance isTopologicalGroup [Π i, Group (R i)] [∀ i, SubgroupClass (S i) (R i)] - [∀ i, IsTopologicalGroup (R i)] : - IsTopologicalGroup (Πʳ i, [R i, B i]) where - -instance isTopologicalRing [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] - [∀ i, IsTopologicalRing (R i)] : - IsTopologicalRing (Πʳ i, [R i, B i]) where - -/-- Assume that each `R i` is a locally compact group with `A i` an open subgroup. -Assume also that all but finitely many `A i`s are compact. -Then the restricted product `Πʳ i, [R i, A i]` is a locally compact group. -/ -@[to_additive -"Assume that each `R i` is a locally compact additive group with `A i` an open subgroup. -Assume also that all but finitely many `A i`s are compact. -Then the restricted product `Πʳ i, [R i, A i]` is a locally compact additive group."] -theorem locallyCompactSpace_of_group [Π i, Group (R i)] [∀ i, SubgroupClass (S i) (R i)] - [∀ i, IsTopologicalGroup (R i)] [∀ i, LocallyCompactSpace (R i)] - (hBcompact : ∀ᶠ i in cofinite, IsCompact (B i : Set (R i))) : - LocallyCompactSpace (Πʳ i, [R i, B i]) := - haveI : WeaklyLocallyCompactSpace (Πʳ i, [R i, B i]) := - weaklyLocallyCompactSpace_of_cofinite hBopen.out hBcompact - inferInstance - -open Pointwise in -@[to_additive] -instance [Π i, Group (R i)] [∀ i, SubgroupClass (S i) (R i)] [∀ i, IsTopologicalGroup (R i)] - [hAcompact : ∀ i, CompactSpace (B i)] : LocallyCompactSpace (Πʳ i, [R i, B i]) := - -- TODO: extract as a lemma - haveI : ∀ i, WeaklyLocallyCompactSpace (R i) := fun i ↦ .mk fun x ↦ - ⟨x • (B i : Set (R i)), .smul _ (isCompact_iff_compactSpace.mpr inferInstance), - hBopen.out i |>.smul _ |>.mem_nhds <| by - simpa using smul_mem_smul_set (a := x) (one_mem (B i))⟩ - locallyCompactSpace_of_group _ <| .of_forall fun _ ↦ isCompact_iff_compactSpace.mpr inferInstance - -end cofinite - -end Compatibility - -section map_continuous - -variable {ι₁ ι₂ : Type*} -variable (R₁ : ι₁ → Type*) (R₂ : ι₂ → Type*) -variable [∀ i, TopologicalSpace (R₁ i)] [∀ i, TopologicalSpace (R₂ i)] -variable {𝓕₁ : Filter ι₁} {𝓕₂ : Filter ι₂} -variable {A₁ : (i : ι₁) → Set (R₁ i)} {A₂ : (i : ι₂) → Set (R₂ i)} -variable (f : ι₂ → ι₁) (hf : Tendsto f 𝓕₂ 𝓕₁) - -variable (φ : ∀ j, R₁ (f j) → R₂ j) (hφ : ∀ᶠ j in 𝓕₂, MapsTo (φ j) (A₁ (f j)) (A₂ j)) - -theorem map_continuous (φ_cont : ∀ j, Continuous (φ j)) : Continuous (map R₁ R₂ f hf φ hφ) := by - rw [continuous_dom] - intro S hS - set T := f ⁻¹' S ∩ {j | MapsTo (φ j) (A₁ (f j)) (A₂ j)} - have hT : 𝓕₂ ≤ 𝓟 T := by - rw [le_principal_iff] at hS ⊢ - exact inter_mem (hf hS) hφ - have hf' : Tendsto f (𝓟 T) (𝓟 S) := by aesop - have hφ' : ∀ᶠ j in 𝓟 T, MapsTo (φ j) (A₁ (f j)) (A₂ j) := by aesop - have key : map R₁ R₂ f hf φ hφ ∘ inclusion R₁ A₁ hS = - inclusion R₂ A₂ hT ∘ map R₁ R₂ f hf' φ hφ' := rfl - rw [key] - exact continuous_inclusion _ |>.comp <| - continuous_rng_of_principal.mpr <| - continuous_pi fun j ↦ φ_cont j |>.comp <| continuous_eval (f j) - -end map_continuous - -end RestrictedProduct +deprecated_module (since := "2025-06-11") diff --git a/Mathlib/Topology/Algebra/RestrictedProduct/Basic.lean b/Mathlib/Topology/Algebra/RestrictedProduct/Basic.lean new file mode 100644 index 00000000000000..60934aec8d727e --- /dev/null +++ b/Mathlib/Topology/Algebra/RestrictedProduct/Basic.lean @@ -0,0 +1,394 @@ +/- +Copyright (c) 2025 Anatole Dedecker. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anatole Dedecker +-/ +import Mathlib.Algebra.Ring.Pi +import Mathlib.Algebra.Ring.Subring.Defs +import Mathlib.GroupTheory.GroupAction.SubMulAction +import Mathlib.Order.Filter.Cofinite -- for `Πʳ i, [R i, A i]` notation, confuses shake + +/-! +# Restricted products of sets, groups and rings + +We define the **restricted product** of `R : ι → Type*` of types, relative to +a family of subsets `A : (i : ι) → Set (R i)` and a filter `𝓕 : Filter ι`. This +is the set of all `x : Π i, R i` such that the set `{j | x j ∈ A j}` belongs to `𝓕`. +We denote it by `Πʳ i, [R i, A i]_[𝓕]`. + +The main case of interest, which we shall refer to as the "classical restricted product", +is that of `𝓕 = cofinite`. Recall that this is the filter of all subsets of `ι`, which are +*cofinite* in the sense that they have finite complement. +Hence, the associated restricted product is the set of all `x : Π i, R i` such that +`x j ∈ A j` for all but finitely many `j`s. We denote it simply by `Πʳ i, [R i, A i]`. + +Another notable case is that of the principal filter `𝓕 = 𝓟 s` corresponding to some subset `s` +of `ι`. The associated restricted product `Πʳ i, [R i, A i]_[𝓟 s]` is the set of all +`x : Π i, R i` such that `x j ∈ A j` for all `j ∈ s`. Put another way, this is just +`(Π i ∈ s, A i) × (Π i ∉ s, R i)`, modulo the obvious isomorphism. + +We endow these types with the obvious algebraic structures. We also show various compatibility +results. + +See also the file Mathlib.Topology.Algebra.RestrictedProduct.TopologicalSpace , which +puts the structure of a topological space on a restricted product of topological spaces. + +## Main definitions + +* `RestrictedProduct`: the restricted product of a family `R` of types, relative to a family `A` of + subsets and a filter `𝓕` on the indexing set. This is denoted `Πʳ i, [R i, A i]_[𝓕]`, + or simply `Πʳ i, [R i, A i]` when `𝓕 = cofinite`. +* `RestrictedProduct.instDFunLike`: interpret an element of `Πʳ i, [R i, A i]_[𝓕]` as an element + of `Π i, R i` using the `DFunLike` machinery. +* `RestrictedProduct.structureMap`: the inclusion map from `Π i, A i` to `Πʳ i, [R i, A i]_[𝓕]`. + +## Notation + +* `Πʳ i, [R i, A i]_[𝓕]` is `RestrictedProduct R A 𝓕`. +* `Πʳ i, [R i, A i]` is `RestrictedProduct R A cofinite`. + +## Tags + +restricted product, adeles, ideles +-/ + +open Set Filter + +variable {ι : Type*} +variable (R : ι → Type*) (A : (i : ι) → Set (R i)) + +/-! +## Definition and elementary maps +-/ + +/-- The **restricted product** of a family `R : ι → Type*` of types, relative to subsets +`A : (i : ι) → Set (R i)` and the filter `𝓕 : Filter ι`, is the set of all `x : Π i, R i` +such that the set `{j | x j ∈ A j}` belongs to `𝓕`. We denote it by `Πʳ i, [R i, A i]_[𝓕]`. + +The most common use case is with `𝓕 = cofinite`, in which case the restricted product is the set +of all `x : Π i, R i` such that `x j ∈ A j` for all but finitely many `j`. We denote it simply +by `Πʳ i, [R i, A i]`. + +Similarly, if `S` is a principal filter, the restricted product `Πʳ i, [R i, A i]_[𝓟 s]` +is the set of all `x : Π i, R i` such that `∀ j ∈ S, x j ∈ A j`. -/ +def RestrictedProduct (𝓕 : Filter ι) : Type _ := {x : Π i, R i // ∀ᶠ i in 𝓕, x i ∈ A i} + +open Batteries.ExtendedBinder + +/-- `Πʳ i, [R i, A i]_[𝓕]` is `RestrictedProduct R A 𝓕`. -/ +scoped[RestrictedProduct] +notation3 "Πʳ "(...)", ""["r:(scoped R => R)", "a:(scoped A => A)"]_[" f "]" => + RestrictedProduct r a f + +/-- `Πʳ i, [R i, A i]` is `RestrictedProduct R A cofinite`. -/ +scoped[RestrictedProduct] +notation3"Πʳ "(...)", ""["r:(scoped R => R)", "a:(scoped A => A)"]" => + RestrictedProduct r a cofinite + +namespace RestrictedProduct + +open scoped RestrictedProduct + +variable {𝓕 𝓖 : Filter ι} + +instance : DFunLike (Πʳ i, [R i, A i]_[𝓕]) ι R where + coe x i := x.1 i + coe_injective' _ _ := Subtype.ext + +@[ext] +lemma ext {x y : Πʳ i, [R i, A i]_[𝓕]} (h : ∀ i, x i = y i) : x = y := + Subtype.ext <| funext h + +lemma range_coe : + range ((↑) : Πʳ i, [R i, A i]_[𝓕] → Π i, R i) = {x | ∀ᶠ i in 𝓕, x i ∈ A i} := + Subtype.range_val_subtype + +lemma range_coe_principal {S : Set ι} : + range ((↑) : Πʳ i, [R i, A i]_[𝓟 S] → Π i, R i) = S.pi A := + range_coe R A + +@[simp] lemma eventually (x : Πʳ i, [R i, A i]_[𝓕]) : ∀ᶠ i in 𝓕, x i ∈ A i := x.2 + +variable (𝓕) in +/-- The *structure map* of the restricted product is the obvious inclusion from `Π i, A i` +into `Πʳ i, [R i, A i]_[𝓕]`. -/ +def structureMap (x : Π i, A i) : Πʳ i, [R i, A i]_[𝓕] := + ⟨fun i ↦ x i, .of_forall fun i ↦ (x i).2⟩ + +/-- If `𝓕 ≤ 𝓖`, the restricted product `Πʳ i, [R i, A i]_[𝓖]` is naturally included in +`Πʳ i, [R i, A i]_[𝓕]`. This is the corresponding map. -/ +def inclusion (h : 𝓕 ≤ 𝓖) (x : Πʳ i, [R i, A i]_[𝓖]) : + Πʳ i, [R i, A i]_[𝓕] := + ⟨x, x.2.filter_mono h⟩ + +variable (𝓕) in +lemma inclusion_eq_id : inclusion R A (le_refl 𝓕) = id := rfl + +lemma exists_inclusion_eq_of_eventually (h : 𝓕 ≤ 𝓖) {x : Πʳ i, [R i, A i]_[𝓕]} + (hx𝓖 : ∀ᶠ i in 𝓖, x i ∈ A i) : + ∃ x' : Πʳ i, [R i, A i]_[𝓖], inclusion R A h x' = x := + ⟨⟨x.1, hx𝓖⟩, rfl⟩ + +lemma exists_structureMap_eq_of_forall {x : Πʳ i, [R i, A i]_[𝓕]} + (hx : ∀ i, x.1 i ∈ A i) : + ∃ x' : Π i, A i, structureMap R A 𝓕 x' = x := + ⟨fun i ↦ ⟨x i, hx i⟩, rfl⟩ + +lemma range_inclusion (h : 𝓕 ≤ 𝓖) : + Set.range (inclusion R A h) = {x | ∀ᶠ i in 𝓖, x i ∈ A i} := + subset_antisymm (range_subset_iff.mpr fun x ↦ x.2) + (fun _ hx ↦ mem_range.mpr <| exists_inclusion_eq_of_eventually R A h hx) + +lemma range_structureMap : + Set.range (structureMap R A 𝓕) = {f | ∀ i, f.1 i ∈ A i} := + subset_antisymm (range_subset_iff.mpr fun x i ↦ (x i).2) + (fun _ hx ↦ mem_range.mpr <| exists_structureMap_eq_of_forall R A hx) + +section Algebra +/-! +## Algebraic instances on restricted products + +In this section, we endow the restricted product with its algebraic instances. +To avoid any unnecessary coercions, we use subobject classes for the subset `B i` of each `R i`. +-/ + +variable {S : ι → Type*} -- subobject type +variable [Π i, SetLike (S i) (R i)] +variable {B : Π i, S i} + +@[to_additive] +instance [Π i, One (R i)] [∀ i, OneMemClass (S i) (R i)] : One (Πʳ i, [R i, B i]_[𝓕]) where + one := ⟨fun _ ↦ 1, .of_forall fun _ ↦ one_mem _⟩ + +@[to_additive (attr := simp)] +lemma one_apply [Π i, One (R i)] [∀ i, OneMemClass (S i) (R i)] (i : ι) : + (1 : Πʳ i, [R i, B i]_[𝓕]) i = 1 := + rfl + +@[to_additive] +instance [Π i, Inv (R i)] [∀ i, InvMemClass (S i) (R i)] : Inv (Πʳ i, [R i, B i]_[𝓕]) where + inv x := ⟨fun i ↦ (x i)⁻¹, x.2.mono fun _ ↦ inv_mem⟩ + +@[to_additive (attr := simp)] +lemma inv_apply [Π i, Inv (R i)] [∀ i, InvMemClass (S i) (R i)] + (x : Πʳ i, [R i, B i]_[𝓕]) (i : ι) : (x⁻¹) i = (x i)⁻¹ := + rfl + +@[to_additive] +instance [Π i, Mul (R i)] [∀ i, MulMemClass (S i) (R i)] : Mul (Πʳ i, [R i, B i]_[𝓕]) where + mul x y := ⟨fun i ↦ x i * y i, y.2.mp (x.2.mono fun _ ↦ mul_mem)⟩ + +@[to_additive (attr := simp)] +lemma mul_apply [Π i, Mul (R i)] [∀ i, MulMemClass (S i) (R i)] + (x y : Πʳ i, [R i, B i]_[𝓕]) (i : ι) : (x * y) i = x i * y i := + rfl + +@[to_additive] +instance {G : Type*} [Π i, SMul G (R i)] [∀ i, SMulMemClass (S i) G (R i)] : + SMul G (Πʳ i, [R i, B i]_[𝓕]) where + smul g x := ⟨fun i ↦ g • (x i), x.2.mono fun _ ↦ SMulMemClass.smul_mem g⟩ + +@[to_additive (attr := simp)] +lemma smul_apply {G : Type*} [Π i, SMul G (R i)] [∀ i, SMulMemClass (S i) G (R i)] (g : G) + (x : Πʳ i, [R i, B i]_[𝓕]) (i : ι) : (g • x) i = g • x i := + rfl + +@[to_additive] +instance [Π i, DivInvMonoid (R i)] [∀ i, SubgroupClass (S i) (R i)] : + Div (Πʳ i, [R i, B i]_[𝓕]) where + div x y := ⟨fun i ↦ x i / y i, y.2.mp (x.2.mono fun _ ↦ div_mem)⟩ + +@[to_additive (attr := simp)] +lemma div_apply [Π i, DivInvMonoid (R i)] [∀ i, SubgroupClass (S i) (R i)] + (x y : Πʳ i, [R i, B i]_[𝓕]) (i : ι) : (x / y) i = x i / y i := + rfl + +instance [Π i, Monoid (R i)] [∀ i, SubmonoidClass (S i) (R i)] : + Pow (Πʳ i, [R i, B i]_[𝓕]) ℕ where + pow x n := ⟨fun i ↦ x i ^ n, x.2.mono fun _ hi ↦ pow_mem hi n⟩ + +lemma pow_apply [Π i, Monoid (R i)] [∀ i, SubmonoidClass (S i) (R i)] + (x : Πʳ i, [R i, B i]_[𝓕]) (n : ℕ) (i : ι) : (x ^ n) i = x i ^ n := + rfl + +instance [Π i, AddMonoid (R i)] [∀ i, AddSubmonoidClass (S i) (R i)] : + AddMonoid (Πʳ i, [R i, B i]_[𝓕]) := + haveI : ∀ i, SMulMemClass (S i) ℕ (R i) := fun _ ↦ AddSubmonoidClass.nsmulMemClass + DFunLike.coe_injective.addMonoid _ rfl (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) + +@[to_additive existing] +instance [Π i, Monoid (R i)] [∀ i, SubmonoidClass (S i) (R i)] : + Monoid (Πʳ i, [R i, B i]_[𝓕]) := + DFunLike.coe_injective.monoid _ rfl (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) + +instance [Π i, DivInvMonoid (R i)] [∀ i, SubgroupClass (S i) (R i)] : + Pow (Πʳ i, [R i, B i]_[𝓕]) ℤ where + pow x n := ⟨fun i ↦ x i ^ n, x.2.mono fun _ hi ↦ zpow_mem hi n⟩ + +lemma zpow_apply [Π i, DivInvMonoid (R i)] [∀ i, SubgroupClass (S i) (R i)] + (x : Πʳ i, [R i, B i]_[𝓕]) (n : ℤ) (i : ι) : (x ^ n) i = x i ^ n := + rfl + +instance [Π i, AddMonoidWithOne (R i)] [∀ i, AddSubmonoidWithOneClass (S i) (R i)] : + NatCast (Πʳ i, [R i, B i]_[𝓕]) where + natCast n := ⟨fun _ ↦ n, .of_forall fun _ ↦ natCast_mem _ n⟩ + +instance [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] : + IntCast (Πʳ i, [R i, B i]_[𝓕]) where + intCast n := ⟨fun _ ↦ n, .of_forall fun _ ↦ intCast_mem _ n⟩ + +instance [Π i, AddGroup (R i)] [∀ i, AddSubgroupClass (S i) (R i)] : + AddGroup (Πʳ i, [R i, B i]_[𝓕]) := + haveI : ∀ i, SMulMemClass (S i) ℤ (R i) := fun _ ↦ AddSubgroupClass.zsmulMemClass + haveI : ∀ i, SMulMemClass (S i) ℕ (R i) := fun _ ↦ AddSubmonoidClass.nsmulMemClass + DFunLike.coe_injective.addGroup _ rfl (fun _ _ ↦ rfl) (fun _ ↦ rfl) (fun _ _ ↦ rfl) + (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) + +@[to_additive existing] +instance [Π i, Group (R i)] [∀ i, SubgroupClass (S i) (R i)] : + Group (Πʳ i, [R i, B i]_[𝓕]) := + DFunLike.coe_injective.group _ rfl (fun _ _ ↦ rfl) (fun _ ↦ rfl) (fun _ _ ↦ rfl) + (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) + +instance [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] : + Ring (Πʳ i, [R i, B i]_[𝓕]) := + DFunLike.coe_injective.ring _ rfl rfl (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) (fun _ ↦ rfl) + (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) (fun _ _ ↦ rfl) (fun _ ↦ rfl) (fun _ ↦ rfl) + +instance [Π i, CommRing (R i)] [∀ i, SubringClass (S i) (R i)] : + CommRing (Πʳ i, [R i, B i]_[𝓕]) where + mul_comm _ _ := DFunLike.coe_injective <| funext (fun _ ↦ mul_comm _ _) + +end Algebra + +section eval + +variable {S : ι → Type*} +variable [Π i, SetLike (S i) (R i)] +variable {B : Π i, S i} + +/-- `RestrictedProduct.evalMonoidHom j` is the monoid homomorphism from the restricted +product `Πʳ i, [R i, B i]_[𝓕]` to the component `R j`. +-/ +@[to_additive "`RestrictedProduct.evalAddMonoidHom j` is the monoid homomorphism from the restricted +product `Πʳ i, [R i, B i]_[𝓕]` to the component `R j`."] +def evalMonoidHom (j : ι) [Π i, Monoid (R i)] [∀ i, SubmonoidClass (S i) (R i)] : + (Πʳ i, [R i, B i]_[𝓕]) →* R j where + toFun x := x j + map_one' := rfl + map_mul' _ _ := rfl + +@[simp] +lemma evalMonoidHom_apply [Π i, Monoid (R i)] [∀ i, SubmonoidClass (S i) (R i)] + (x : Πʳ i, [R i, B i]_[𝓕]) (j : ι) : evalMonoidHom R j x = x j := + rfl + +/-- `RestrictedProduct.evalRingHom j` is the ring homomorphism from the restricted +product `Πʳ i, [R i, B i]_[𝓕]` to the component `R j`. +-/ +def evalRingHom (j : ι) [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] : + (Πʳ i, [R i, B i]_[𝓕]) →+* R j where + __ := evalMonoidHom R j + __ := evalAddMonoidHom R j + +@[simp] +lemma evalRingHom_apply [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] + (x : Πʳ i, [R i, B i]_[𝓕]) (j : ι) : evalRingHom R j x = x j := + rfl + +end eval + +section map + +variable {ι₁ ι₂ : Type*} +variable (R₁ : ι₁ → Type*) (R₂ : ι₂ → Type*) +variable {𝓕₁ : Filter ι₁} {𝓕₂ : Filter ι₂} +variable {A₁ : (i : ι₁) → Set (R₁ i)} {A₂ : (i : ι₂) → Set (R₂ i)} +variable {S₁ : ι₁ → Type*} {S₂ : ι₂ → Type*} +variable [Π i, SetLike (S₁ i) (R₁ i)] [Π j, SetLike (S₂ j) (R₂ j)] +variable {B₁ : Π i, S₁ i} {B₂ : Π j, S₂ j} +variable (f : ι₂ → ι₁) (hf : Tendsto f 𝓕₂ 𝓕₁) + +section set + +variable (φ : ∀ j, R₁ (f j) → R₂ j) (hφ : ∀ᶠ j in 𝓕₂, MapsTo (φ j) (A₁ (f j)) (A₂ j)) + +/-- +Given two restricted products `Πʳ (i : ι₁), [R₁ i, A₁ i]_[𝓕₁]` and `Πʳ (j : ι₂), [R₂ j, A₂ j]_[𝓕₂]`, +`RestrictedProduct.map` gives a function between them. The data needed is a function `f : ι₂ → ι₁` +such that `𝓕₂` tends to `𝓕₁` along `f`, and functions `φ j : R₁ (f j) → R₂ j` +sending `A₁ (f j)` into `A₂ j` for an `𝓕₂`-large set of `j`'s. + +See also `mapMonoidHom`, `mapAddMonoidHom` and `mapRingHom` for variants. +-/ +def map (x : Πʳ i, [R₁ i, A₁ i]_[𝓕₁]) : Πʳ j, [R₂ j, A₂ j]_[𝓕₂] := ⟨fun j ↦ φ j (x (f j)), by + filter_upwards [hf.eventually x.2, hφ] using fun _ h1 h2 ↦ h2 h1⟩ + +@[simp] +lemma map_apply (x : Πʳ i, [R₁ i, A₁ i]_[𝓕₁]) (j : ι₂) : + x.map R₁ R₂ f hf φ hφ j = φ j (x (f j)) := + rfl + +end set + +section monoid + +variable [Π i, Monoid (R₁ i)] [Π i, Monoid (R₂ i)] [∀ i, SubmonoidClass (S₁ i) (R₁ i)] + [∀ i, SubmonoidClass (S₂ i) (R₂ i)] (φ : ∀ j, R₁ (f j) →* R₂ j) + (hφ : ∀ᶠ j in 𝓕₂, MapsTo (φ j) (B₁ (f j)) (B₂ j)) + +/-- +Given two restricted products `Πʳ (i : ι₁), [R₁ i, B₁ i]_[𝓕₁]` and `Πʳ (j : ι₂), [R₂ j, B₂ j]_[𝓕₂]`, +`RestrictedProduct.mapMonoidHom` gives a monoid homomorphism between them. The data needed is a +function `f : ι₂ → ι₁` such that `𝓕₂` tends to `𝓕₁` along `f`, and monoid homomorphisms +`φ j : R₁ (f j) → R₂ j` sending `B₁ (f j)` into `B₂ j` for an `𝓕₂`-large set of `j`'s. +-/ +@[to_additive " +Given two restricted products `Πʳ (i : ι₁), [R₁ i, B₁ i]_[𝓕₁]` and `Πʳ (j : ι₂), [R₂ j, B₂ j]_[𝓕₂]`, +`RestrictedProduct.mapAddMonoidHom` gives a additive monoid homomorphism between them. The data +needed is a function `f : ι₂ → ι₁` such that `𝓕₂` tends to `𝓕₁` along `f`, and +additive monoid homomorphisms `φ j : R₁ (f j) → R₂ j` sending `B₁ (f j)` into `B₂ j` for +an `𝓕₂`-large set of `j`'s. +"] +def mapMonoidHom : Πʳ i, [R₁ i, B₁ i]_[𝓕₁] →* Πʳ j, [R₂ j, B₂ j]_[𝓕₂] where + toFun := map R₁ R₂ f hf (fun j r ↦ φ j r) hφ + map_one' := by + ext i + exact map_one (φ i) + map_mul' x y := by + ext i + exact map_mul (φ i) _ _ + +@[to_additive (attr := simp)] +lemma mapMonoidHom_apply (x : Πʳ i, [R₁ i, B₁ i]_[𝓕₁]) (j : ι₂) : + x.mapMonoidHom R₁ R₂ f hf φ hφ j = φ j (x (f j)) := + rfl + +end monoid + +section ring + +variable [Π i, Ring (R₁ i)] [Π i, Ring (R₂ i)] [∀ i, SubringClass (S₁ i) (R₁ i)] + [∀ i, SubringClass (S₂ i) (R₂ i)] (φ : ∀ j, R₁ (f j) →+* R₂ j) + (hφ : ∀ᶠ j in 𝓕₂, MapsTo (φ j) (B₁ (f j)) (B₂ j)) + +/-- +Given two restricted products `Πʳ (i : ι₁), [R₁ i, B₁ i]_[𝓕₁]` and `Πʳ (j : ι₂), [R₂ j, B₂ j]_[𝓕₂]`, +`RestrictedProduct.mapRingHom` gives a ring homomorphism between them. The data needed is a +function `f : ι₂ → ι₁` such that `𝓕₂` tends to `𝓕₁` along `f`, and ring homomorphisms +`φ j : R₁ (f j) → R₂ j` sending `B₁ (f j)` into `B₂ j` for an `𝓕₂`-large set of `j`'s. +-/ +def mapRingHom : Πʳ i, [R₁ i, B₁ i]_[𝓕₁] →+* Πʳ j, [R₂ j, B₂ j]_[𝓕₂] where + __ := mapMonoidHom R₁ R₂ f hf (fun j ↦ φ j) hφ + __ := mapAddMonoidHom R₁ R₂ f hf (fun j ↦ φ j) hφ + +@[simp] +lemma mapRingHom_apply (x : Πʳ i, [R₁ i, B₁ i]_[𝓕₁]) (j : ι₂) : + x.mapRingHom R₁ R₂ f hf φ hφ j = φ j (x (f j)) := + rfl + +end ring + +end map + +end RestrictedProduct diff --git a/Mathlib/Topology/Algebra/RestrictedProduct/TopologicalSpace.lean b/Mathlib/Topology/Algebra/RestrictedProduct/TopologicalSpace.lean new file mode 100644 index 00000000000000..80623222beeffd --- /dev/null +++ b/Mathlib/Topology/Algebra/RestrictedProduct/TopologicalSpace.lean @@ -0,0 +1,667 @@ +/- +Copyright (c) 2025 Anatole Dedecker. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anatole Dedecker +-/ +import Mathlib.Topology.Algebra.Group.Pointwise +import Mathlib.Topology.Algebra.RestrictedProduct.Basic +import Mathlib.Topology.Algebra.Ring.Basic + +/-! +# Restricted products of topological spaces, topological groups and rings + +We endow a restricted product of topological spaces with a natural topology, +which we describe below. We also show various compatibility results when we change +filters, and extend the construction of restricted products of algebraic structures +to the topological setting. + +In particular, with the theory of adeles in mind, we show that if each `R i` is a locally compact +topological ring with open subring `A i`, and if all but finitely many of the `A i`s are also +compact, then `Πʳ i, [R i, A i]` is a locally compact topological ring. + +## Main definitions + +* `RestrictedProduct.topologicalSpace`: the `TopologicalSpace` instance on + the restricted product `Πʳ i, [R i, A i]_[𝓕]`. + +## Topology on the restricted product + +The topology on the restricted product `Πʳ i, [R i, A i]_[𝓕]` is defined in the following way: +1. If `𝓕` is some principal filter `𝓟 s`, recall that `Πʳ i, [R i, A i]_[𝓟 s]` is canonically +identified with `(Π i ∈ s, A i) × (Π i ∉ s, R i)`. We endow it with the product topology, +which is also the topology induced from the full product `Π i, R i`. +2. In general, we note that `𝓕` is the infimum of the principal filters coarser than `𝓕`. We +then endow `Πʳ i, [R i, A i]_[𝓕]` with the inductive limit / final topology associated to the +inclusion maps `Πʳ i, [R i, A i]_[𝓟 s] → Πʳ i, [R i, A i]_[𝓕]` where `𝓕 ≤ 𝓟 s`. + +In particular: +* On the classical restricted product, with respect to the cofinite filter, this corresponds to + taking the inductive limit of the `Πʳ i, [R i, A i]_[𝓟 s]` over all *cofinite* sets `s : Set ι`. +* If `𝓕 = 𝓟 s` is a principal filter, this second step clearly does not change the topology, since + `s` belongs to the indexing set of the inductive limit. + +Taking advantage of that second remark, we do not actually declare an instance specific to +principal filters. Instead, we provide directly the general instance (corresponding to step 2 above) +as `RestrictedProduct.topologicalSpace`. We then prove that, for a principal filter, the +map to the full product is an inducing (`RestrictedProduct.isEmbedding_coe_of_principal`), +and that the topology for a general `𝓕` is indeed the expected inductive limit +(`RestrictedProduct.topologicalSpace_eq_iSup`). + +## Main statements + +* `RestrictedProduct.isEmbedding_coe_of_principal`: for any set `S`, `Πʳ i, [R i, A i]_[𝓟 S]` + is endowed with the subset topology coming from `Π i, R i`. +* `RestrictedProduct.topologicalSpace_eq_iSup`: the topology on `Πʳ i, [R i, A i]_[𝓕]` is the + inductive limit / final topology associated to the natural maps + `Πʳ i, [R i, A i]_[𝓟 S] → Πʳ i, [R i, A i]_[𝓕]`, where `𝓕 ≤ 𝓟 S`. +* `RestrictedProduct.continuous_dom`: a map from `Πʳ i, [R i, A i]_[𝓕]` is continuous +*if and only if* its restriction to each `Πʳ i, [R i, A i]_[𝓟 s]` (with `𝓕 ≤ 𝓟 s`) is continuous. +* `RestrictedProduct.continuous_dom_prod_left`: assume that each `A i` is an **open** subset of +`R i`. Then, for any topological space `Y`, a map from `Y × Πʳ i, [R i, A i]` is continuous +*if and only if* its restriction to each `Y × Πʳ i, [R i, A i]_[𝓟 S]` (with `S` cofinite) +is continuous. + +* `RestrictedProduct.isTopologicalGroup`: if each `R i` is a topological group and each `A i` is an + open subgroup of `R i`, then `Πʳ i, [R i, A i]` is a topological group. +* `RestrictedProduct.isTopologicalRing`: if each `R i` is a topological ring and each `A i` is an + open subring of `R i`, then `Πʳ i, [R i, A i]` is a topological ring. +* `RestrictedProduct.continuousSMul`: if some topological monoid `G` acts on each `M i`, and each + `A i` is stable for that action, then the natural action of `G` on `Πʳ i, [M i, A i]` is also + continuous. In particular, if each `M i` is a topological `R`-module and each `A i` is an open + sub-`R`-module of `M i`, then `Πʳ i, [M i, A i]` is a topological `R`-module. + +* `RestrictedProduct.weaklyLocallyCompactSpace_of_cofinite`: if each `R i` is weakly locally + compact, each `A i` is open, and all but finitely many `A i`s are also compact, then the + restricted product `Πʳ i, [R i, A i]` is weakly locally compact. +* `RestrictedProduct.locallyCompactSpace_of_group`: assume that each `R i` is a locally compact + group with `A i` an open subgroup. Assume also that all but finitely many `A i`s are compact. + Then the restricted product `Πʳ i, [R i, A i]` is a locally compact group. + +## Implementation details + +Outside of principal filters and the cofinite filter, the topology we define on the restricted +product does not seem well-behaved. While declaring a single instance is practical, it may conflict +with more interesting topologies in some other cases. Thus, future contributions should not +restrain from specializing these instances to principal and cofinite filters if necessary. + +## Tags + +restricted product, adeles, ideles +-/ + +open Set Topology Filter + +variable {ι : Type*} +variable (R : ι → Type*) (A : (i : ι) → Set (R i)) + +namespace RestrictedProduct + +open scoped RestrictedProduct + +variable {𝓕 𝓖 : Filter ι} + +section Topology +/-! +## Topology on the restricted product + +The topology on the restricted product `Πʳ i, [R i, A i]_[𝓕]` is defined in the following way: +1. If `𝓕` is some principal filter `𝓟 s`, recall that `Πʳ i, [R i, A i]_[𝓟 s]` is canonically +identified with `(Π i ∈ s, A i) × (Π i ∉ s, R i)`. We endow it with the product topology, +which is also the topology induced from the full product `Π i, R i`. +2. In general, we note that `𝓕` is the infimum of the principal filters coarser than `𝓕`. We +then endow `Πʳ i, [R i, A i]_[𝓕]` with the inductive limit / final topology associated to the +inclusion maps `Πʳ i, [R i, A i]_[𝓟 s] → Πʳ i, [R i, A i]_[𝓕]` where `𝓕 ≤ 𝓟 s`. + +In particular: +* On the classical restricted product, with respect to the cofinite filter, this corresponds to + taking the inductive limit of the `Πʳ i, [R i, A i]_[𝓟 s]` over all *cofinite* sets `s : Set ι`. +* If `𝓕 = 𝓟 s` is a principal filter, this second step clearly does not change the topology, since + `s` belongs to the indexing set of the inductive limit. + +Taking advantage of that second remark, we do not actually declare an instance specific to +principal filters. Instead, we provide directly the general instance (corresponding to step 2 above) +as `RestrictedProduct.topologicalSpace`. We then prove that, for a principal filter, the +map to the full product is an inducing (`RestrictedProduct.isEmbedding_coe_of_principal`), +and that the topology for a general `𝓕` is indeed the expected inductive limit +(`RestrictedProduct.topologicalSpace_eq_iSup`). + +Note: outside of these two cases, this topology on the restricted product does not seem +well-behaved. While declaring a single instance is practical, it may conflict with more interesting +topologies in some other cases. Thus, future contributions should not restrain from specializing +these instances to principal and cofinite filters if necessary. +-/ + +/-! +### Definition of the topology +-/ + +variable {R A R' A'} +variable {𝓕 : Filter ι} +variable [∀ i, TopologicalSpace (R i)] + +variable (R A 𝓕) in +instance topologicalSpace : TopologicalSpace (Πʳ i, [R i, A i]_[𝓕]) := + ⨆ (S : Set ι) (hS : 𝓕 ≤ 𝓟 S), .coinduced (inclusion R A hS) + (.induced ((↑) : Πʳ i, [R i, A i]_[𝓟 S] → Π i, R i) inferInstance) + +@[fun_prop] +theorem continuous_coe : + Continuous ((↑) : Πʳ i, [R i, A i]_[𝓕] → Π i, R i) := + continuous_iSup_dom.mpr fun _ ↦ continuous_iSup_dom.mpr fun _ ↦ + continuous_coinduced_dom.mpr continuous_induced_dom + +@[fun_prop] +theorem continuous_eval (i : ι) : + Continuous (fun (x : Πʳ i, [R i, A i]_[𝓕]) ↦ x i) := + continuous_apply _ |>.comp continuous_coe + +@[fun_prop] +theorem continuous_inclusion {𝓖 : Filter ι} (h : 𝓕 ≤ 𝓖) : + Continuous (inclusion R A h) := by + simp_rw [continuous_iff_coinduced_le, topologicalSpace, coinduced_iSup, coinduced_compose] + exact iSup₂_le fun S hS ↦ le_iSup₂_of_le S (le_trans h hS) le_rfl + +instance [∀ i, T0Space (R i)] : T0Space (Πʳ i, [R i, A i]_[𝓕]) := + t0Space_of_injective_of_continuous DFunLike.coe_injective continuous_coe + +instance [∀ i, T1Space (R i)] : T1Space (Πʳ i, [R i, A i]_[𝓕]) := + t1Space_of_injective_of_continuous DFunLike.coe_injective continuous_coe + +instance [∀ i, T2Space (R i)] : T2Space (Πʳ i, [R i, A i]_[𝓕]) := + .of_injective_continuous DFunLike.coe_injective continuous_coe + +section principal +/-! +### Topological facts in the principal case +-/ + +variable {S : Set ι} + +theorem topologicalSpace_eq_of_principal : + topologicalSpace R A (𝓟 S) = + .induced ((↑) : Πʳ i, [R i, A i]_[𝓟 S] → Π i, R i) inferInstance := + le_antisymm (continuous_iff_le_induced.mp continuous_coe) <| + (le_iSup₂_of_le S le_rfl <| by rw [inclusion_eq_id R A (𝓟 S), @coinduced_id]) + +theorem topologicalSpace_eq_of_top : + topologicalSpace R A ⊤ = + .induced ((↑) : Πʳ i, [R i, A i]_[⊤] → Π i, R i) inferInstance := + principal_univ ▸ topologicalSpace_eq_of_principal + +theorem topologicalSpace_eq_of_bot : + topologicalSpace R A ⊥ = + .induced ((↑) : Πʳ i, [R i, A i]_[⊥] → Π i, R i) inferInstance := + principal_empty ▸ topologicalSpace_eq_of_principal + +theorem isEmbedding_coe_of_principal : + IsEmbedding ((↑) : Πʳ i, [R i, A i]_[𝓟 S] → Π i, R i) where + eq_induced := topologicalSpace_eq_of_principal + injective := DFunLike.coe_injective + +theorem isEmbedding_coe_of_top : + IsEmbedding ((↑) : Πʳ i, [R i, A i]_[⊤] → Π i, R i) := + principal_univ ▸ isEmbedding_coe_of_principal + +theorem isEmbedding_coe_of_bot : + IsEmbedding ((↑) : Πʳ i, [R i, A i]_[⊥] → Π i, R i) := + principal_empty ▸ isEmbedding_coe_of_principal + +theorem continuous_rng_of_principal {X : Type*} [TopologicalSpace X] + {f : X → Πʳ i, [R i, A i]_[𝓟 S]} : + Continuous f ↔ Continuous ((↑) ∘ f : X → Π i, R i) := + isEmbedding_coe_of_principal.continuous_iff + +theorem continuous_rng_of_top {X : Type*} [TopologicalSpace X] + {f : X → Πʳ i, [R i, A i]_[⊤]} : + Continuous f ↔ Continuous ((↑) ∘ f : X → Π i, R i) := + isEmbedding_coe_of_top.continuous_iff + +theorem continuous_rng_of_bot {X : Type*} [TopologicalSpace X] + {f : X → Πʳ i, [R i, A i]_[⊥]} : + Continuous f ↔ Continuous ((↑) ∘ f : X → Π i, R i) := + isEmbedding_coe_of_bot.continuous_iff + +lemma continuous_rng_of_principal_iff_forall {X : Type*} [TopologicalSpace X] + {f : X → Πʳ (i : ι), [R i, A i]_[𝓟 S]} : + Continuous f ↔ ∀ i : ι, Continuous ((fun x ↦ x i) ∘ f) := + continuous_rng_of_principal.trans continuous_pi_iff + +/-- The obvious bijection between `Πʳ i, [R i, A i]_[⊤]` and `Π i, A i` is a homeomorphism. -/ +def homeoTop : (Π i, A i) ≃ₜ (Πʳ i, [R i, A i]_[⊤]) where + toFun f := ⟨fun i ↦ f i, fun i ↦ (f i).2⟩ + invFun f i := ⟨f i, f.2 i⟩ + continuous_toFun := continuous_rng_of_top.mpr <| continuous_pi fun i ↦ + continuous_subtype_val.comp <| continuous_apply i + continuous_invFun := continuous_pi fun i ↦ continuous_induced_rng.mpr <| continuous_eval i + +/-- The obvious bijection between `Πʳ i, [R i, A i]_[⊥]` and `Π i, R i` is a homeomorphism. -/ +def homeoBot : (Π i, R i) ≃ₜ (Πʳ i, [R i, A i]_[⊥]) where + toFun f := ⟨fun i ↦ f i, eventually_bot⟩ + invFun f i := f i + continuous_toFun := continuous_rng_of_bot.mpr <| continuous_pi fun i ↦ continuous_apply i + continuous_invFun := continuous_pi continuous_eval + +/-- Assume that `S` is a subset of `ι` with finite complement, that each `R i` is weakly locally +compact, and that `A i` is *compact* for all `i ∈ S`. Then the restricted product +`Πʳ i, [R i, A i]_[𝓟 S]` is locally compact. + +Note: we spell "`S` has finite complement" as `cofinite ≤ 𝓟 S`. -/ +theorem weaklyLocallyCompactSpace_of_principal [∀ i, WeaklyLocallyCompactSpace (R i)] + (hS : cofinite ≤ 𝓟 S) (hAcompact : ∀ i ∈ S, IsCompact (A i)) : + WeaklyLocallyCompactSpace (Πʳ i, [R i, A i]_[𝓟 S]) where + exists_compact_mem_nhds := fun x ↦ by + rw [le_principal_iff, mem_cofinite] at hS + classical + have : ∀ i, ∃ K, IsCompact K ∧ K ∈ 𝓝 (x i) := fun i ↦ exists_compact_mem_nhds (x i) + choose K K_compact hK using this + set Q : Set (Π i, R i) := univ.pi (fun i ↦ if i ∈ S then A i else K i) with Q_def + have Q_compact : IsCompact Q := isCompact_univ_pi fun i ↦ by + split_ifs with his + · exact hAcompact i his + · exact K_compact i + set U : Set (Π i, R i) := Sᶜ.pi K + have U_nhds : U ∈ 𝓝 (x : Π i, R i) := set_pi_mem_nhds hS fun i _ ↦ hK i + have QU : (↑) ⁻¹' U ⊆ ((↑) ⁻¹' Q : Set (Πʳ i, [R i, A i]_[𝓟 S])) := fun y H i _ ↦ by + dsimp only + split_ifs with hi + · exact y.2 hi + · exact H i hi + refine ⟨((↑) ⁻¹' Q), ?_, mem_of_superset ?_ QU⟩ + · refine isEmbedding_coe_of_principal.isCompact_preimage_iff ?_ |>.mpr Q_compact + simp_rw [range_coe_principal, Q_def, pi_if, mem_univ, true_and] + exact inter_subset_left + · simpa only [isEmbedding_coe_of_principal.nhds_eq_comap] using preimage_mem_comap U_nhds + +instance [∀ i, WeaklyLocallyCompactSpace (R i)] [hS : Fact (cofinite ≤ 𝓟 S)] + [hAcompact : ∀ i, CompactSpace (A i)] : + WeaklyLocallyCompactSpace (Πʳ i, [R i, A i]_[𝓟 S]) := + weaklyLocallyCompactSpace_of_principal hS.out + fun _ _ ↦ isCompact_iff_compactSpace.mpr inferInstance + +end principal + +section general +/-! +### Topological facts in the general case +-/ + +variable (𝓕) in +theorem topologicalSpace_eq_iSup : + topologicalSpace R A 𝓕 = ⨆ (S : Set ι) (hS : 𝓕 ≤ 𝓟 S), + .coinduced (inclusion R A hS) (topologicalSpace R A (𝓟 S)) := by + simp_rw [topologicalSpace_eq_of_principal, topologicalSpace] + +/-- The **universal property** of the topology on the restricted product: a map from +`Πʳ i, [R i, A i]_[𝓕]` is continuous *iff* its restriction to each `Πʳ i, [R i, A i]_[𝓟 s]` +(with `𝓕 ≤ 𝓟 s`) is continuous. + +See also `RestrictedProduct.continuous_dom_prod_left`. -/ +theorem continuous_dom {X : Type*} [TopologicalSpace X] + {f : Πʳ i, [R i, A i]_[𝓕] → X} : + Continuous f ↔ ∀ (S : Set ι) (hS : 𝓕 ≤ 𝓟 S), Continuous (f ∘ inclusion R A hS) := by + simp_rw [topologicalSpace_eq_of_principal, continuous_iSup_dom, continuous_coinduced_dom] + +theorem isEmbedding_inclusion_principal {S : Set ι} (hS : 𝓕 ≤ 𝓟 S) : + IsEmbedding (inclusion R A hS) := + .of_comp (continuous_inclusion hS) continuous_coe isEmbedding_coe_of_principal + +theorem isEmbedding_inclusion_top : + IsEmbedding (inclusion R A (le_top : 𝓕 ≤ ⊤)) := + .of_comp (continuous_inclusion _) continuous_coe isEmbedding_coe_of_top + +/-- `Π i, A i` has the subset topology from the restricted product. -/ +theorem isEmbedding_structureMap : + IsEmbedding (structureMap R A 𝓕) := + isEmbedding_inclusion_top.comp homeoTop.isEmbedding + +end general + +section cofinite +/-! +### Topological facts in the case where `𝓕 = cofinite` and all `A i`s are open + +The classical restricted product, associated to the cofinite filter, satisfies more topological +properties when each `A i` is an open subset of `R i`. The key fact is that each +`Πʳ i, [R i, A i]_[𝓟 S]` (with `S` cofinite) then embeds **as an open subset** in +`Πʳ i, [R i, A i]`. + +This allows us to prove a "universal property with parameters", expressing that for any +arbitrary topolgical space `X` (of "parameters"), the product `X × Πʳ i, [R i, A i]` +is still the inductive limit of the `X × Πʳ i, [R i, A i]_[𝓟 S]` for `S` cofinite. + +This fact, which is **not true** for a general inductive limit, will allow us to prove continuity +of functions of two variables (e.g algebraic operations), which would otherwise be inaccessible. +-/ + +variable (hAopen : ∀ i, IsOpen (A i)) + +include hAopen in +theorem isOpen_forall_imp_mem_of_principal {S : Set ι} (hS : cofinite ≤ 𝓟 S) {p : ι → Prop} : + IsOpen {f : Πʳ i, [R i, A i]_[𝓟 S] | ∀ i, p i → f.1 i ∈ A i} := by + rw [le_principal_iff] at hS + convert isOpen_set_pi (hS.inter_of_left {i | p i}) (fun i _ ↦ hAopen i) |>.preimage continuous_coe + ext f + refine ⟨fun H i hi ↦ H i hi.2, fun H i hiT ↦ ?_⟩ + by_cases hiS : i ∈ S + · exact f.2 hiS + · exact H i ⟨hiS, hiT⟩ + +include hAopen in +theorem isOpen_forall_mem_of_principal {S : Set ι} (hS : cofinite ≤ 𝓟 S) : + IsOpen {f : Πʳ i, [R i, A i]_[𝓟 S] | ∀ i, f.1 i ∈ A i} := by + convert isOpen_forall_imp_mem_of_principal hAopen hS (p := fun _ ↦ True) + simp + +include hAopen in +theorem isOpen_forall_imp_mem {p : ι → Prop} : + IsOpen {f : Πʳ i, [R i, A i] | ∀ i, p i → f.1 i ∈ A i} := by + simp_rw [topologicalSpace_eq_iSup cofinite, isOpen_iSup_iff, isOpen_coinduced] + exact fun S hS ↦ isOpen_forall_imp_mem_of_principal hAopen hS + +include hAopen in +theorem isOpen_forall_mem : + IsOpen {f : Πʳ i, [R i, A i] | ∀ i, f.1 i ∈ A i} := by + simp_rw [topologicalSpace_eq_iSup cofinite, isOpen_iSup_iff, isOpen_coinduced] + exact fun S hS ↦ isOpen_forall_mem_of_principal hAopen hS + +include hAopen in +theorem isOpenEmbedding_inclusion_principal {S : Set ι} (hS : cofinite ≤ 𝓟 S) : + IsOpenEmbedding (inclusion R A hS) where + toIsEmbedding := isEmbedding_inclusion_principal hS + isOpen_range := by + rw [range_inclusion] + exact isOpen_forall_imp_mem hAopen + +include hAopen in +/-- `Π i, A i` is homeomorphic to an open subset of the restricted product. -/ +theorem isOpenEmbedding_structureMap : + IsOpenEmbedding (structureMap R A cofinite) where + toIsEmbedding := isEmbedding_structureMap + isOpen_range := by + rw [range_structureMap] + exact isOpen_forall_mem hAopen + +include hAopen in +theorem nhds_eq_map_inclusion {S : Set ι} (hS : cofinite ≤ 𝓟 S) + (x : Πʳ i, [R i, A i]_[𝓟 S]) : + (𝓝 (inclusion R A hS x)) = .map (inclusion R A hS) (𝓝 x) := by + rw [isOpenEmbedding_inclusion_principal hAopen hS |>.map_nhds_eq x] + +include hAopen in +theorem nhds_eq_map_structureMap + (x : Π i, A i) : + (𝓝 (structureMap R A cofinite x)) = .map (structureMap R A cofinite) (𝓝 x) := by + rw [isOpenEmbedding_structureMap hAopen |>.map_nhds_eq x] + +include hAopen in +/-- If each `R i` is weakly locally compact, each `A i` is open, and all but finitely many `A i`s +are also compact, then the restricted product `Πʳ i, [R i, A i]` is weakly locally compact. -/ +theorem weaklyLocallyCompactSpace_of_cofinite [∀ i, WeaklyLocallyCompactSpace (R i)] + (hAcompact : ∀ᶠ i in cofinite, IsCompact (A i)) : + WeaklyLocallyCompactSpace (Πʳ i, [R i, A i]) where + exists_compact_mem_nhds := fun x ↦ by + set S := {i | IsCompact (A i) ∧ x i ∈ A i} + have hS : cofinite ≤ 𝓟 S := le_principal_iff.mpr (hAcompact.and x.2) + have hSx : ∀ i ∈ S, x i ∈ A i := fun i hi ↦ hi.2 + have hSA : ∀ i ∈ S, IsCompact (A i) := fun i hi ↦ hi.1 + haveI := weaklyLocallyCompactSpace_of_principal hS hSA + rcases exists_inclusion_eq_of_eventually R A hS hSx with ⟨x', hxx'⟩ + rw [← hxx', nhds_eq_map_inclusion hAopen] + rcases exists_compact_mem_nhds x' with ⟨K, K_compact, hK⟩ + exact ⟨inclusion R A hS '' K, K_compact.image (continuous_inclusion hS), image_mem_map hK⟩ + +instance [hAopen : Fact (∀ i, IsOpen (A i))] [∀ i, WeaklyLocallyCompactSpace (R i)] + [hAcompact : ∀ i, CompactSpace (A i)] : + WeaklyLocallyCompactSpace (Πʳ i, [R i, A i]) := + weaklyLocallyCompactSpace_of_cofinite hAopen.out <| + .of_forall fun _ ↦ isCompact_iff_compactSpace.mpr inferInstance + +include hAopen in +/-- The **universal property with parameters** of the topology on the restricted product: +for any topological space `Y` of "parameters", a map from `(Πʳ i, [R i, A i]) × Y` is continuous +*iff* its restriction to each `(Πʳ i, [R i, A i]_[𝓟 S]) × Y` (with `S` cofinite) is continuous. -/ +theorem continuous_dom_prod_right {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] + {f : Πʳ i, [R i, A i] × Y → X} : + Continuous f ↔ ∀ (S : Set ι) (hS : cofinite ≤ 𝓟 S), + Continuous (f ∘ (Prod.map (inclusion R A hS) id)) := by + refine ⟨fun H S hS ↦ H.comp ((continuous_inclusion hS).prodMap continuous_id), + fun H ↦ ?_⟩ + simp_rw [continuous_iff_continuousAt, ContinuousAt] + rintro ⟨x, y⟩ + set S : Set ι := {i | x i ∈ A i} + have hS : cofinite ≤ 𝓟 S := le_principal_iff.mpr x.2 + have hxS : ∀ i ∈ S, x i ∈ A i := fun i hi ↦ hi + rcases exists_inclusion_eq_of_eventually R A hS hxS with ⟨x', hxx'⟩ + rw [← hxx', nhds_prod_eq, nhds_eq_map_inclusion hAopen hS x', + ← Filter.map_id (f := 𝓝 y), prod_map_map_eq, ← nhds_prod_eq, tendsto_map'_iff] + exact H S hS |>.tendsto ⟨x', y⟩ + +-- TODO: get from the previous one instead of copy-pasting +include hAopen in +/-- The **universal property with parameters** of the topology on the restricted product: +for any topological space `Y` of "parameters", a map from `Y × Πʳ i, [R i, A i]` is continuous +*iff* its restriction to each `Y × Πʳ i, [R i, A i]_[𝓟 S]` (with `S` cofinite) is continuous. -/ +theorem continuous_dom_prod_left {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] + {f : Y × Πʳ i, [R i, A i] → X} : + Continuous f ↔ ∀ (S : Set ι) (hS : cofinite ≤ 𝓟 S), + Continuous (f ∘ (Prod.map id (inclusion R A hS))) := by + refine ⟨fun H S hS ↦ H.comp (continuous_id.prodMap (continuous_inclusion hS)), + fun H ↦ ?_⟩ + simp_rw [continuous_iff_continuousAt, ContinuousAt] + rintro ⟨y, x⟩ + set S : Set ι := {i | x i ∈ A i} + have hS : cofinite ≤ 𝓟 S := le_principal_iff.mpr x.2 + have hxS : ∀ i ∈ S, x i ∈ A i := fun i hi ↦ hi + rcases exists_inclusion_eq_of_eventually R A hS hxS with ⟨x', hxx'⟩ + rw [← hxx', nhds_prod_eq, nhds_eq_map_inclusion hAopen hS x', + ← Filter.map_id (f := 𝓝 y), prod_map_map_eq, ← nhds_prod_eq, tendsto_map'_iff] + exact H S hS |>.tendsto ⟨y, x'⟩ + +include hAopen in +/-- A map from `Πʳ i, [R i, A i] × Πʳ i, [R' i, A' i]` is continuous +*iff* its restriction to each `Πʳ i, [R i, A i]_[𝓟 S] × Πʳ i, [R' i, A' i]_[𝓟 S]` +(with `S` cofinite) is continuous. + +This is the key result for continuity of multiplication and addition. -/ +theorem continuous_dom_prod {R' : ι → Type*} {A' : (i : ι) → Set (R' i)} + [∀ i, TopologicalSpace (R' i)] (hAopen' : ∀ i, IsOpen (A' i)) + {X : Type*} [TopologicalSpace X] + {f : Πʳ i, [R i, A i] × Πʳ i, [R' i, A' i] → X} : + Continuous f ↔ ∀ (S : Set ι) (hS : cofinite ≤ 𝓟 S), + Continuous (f ∘ (Prod.map (inclusion R A hS) (inclusion R' A' hS))) := by + simp_rw [continuous_dom_prod_right hAopen, continuous_dom_prod_left hAopen'] + refine ⟨fun H S hS ↦ H S hS S hS, fun H S hS T hT ↦ ?_⟩ + set U := S ∩ T + have hU : cofinite ≤ 𝓟 (S ∩ T) := inf_principal ▸ le_inf hS hT + have hSU : 𝓟 U ≤ 𝓟 S := principal_mono.mpr inter_subset_left + have hTU : 𝓟 U ≤ 𝓟 T := principal_mono.mpr inter_subset_right + exact (H U hU).comp ((continuous_inclusion hSU).prodMap (continuous_inclusion hTU)) + +/-- A finitary (instead of binary) version of `continuous_dom_prod`. -/ +theorem continuous_dom_pi {n : Type*} [Fintype n] {X : Type*} + [TopologicalSpace X] {A : n → ι → Type*} + [∀ j i, TopologicalSpace (A j i)] + {C : (j : n) → (i : ι) → Set (A j i)} + (hCopen : ∀ j i, IsOpen (C j i)) + {f : (Π j : n, Πʳ i : ι, [A j i, C j i]) → X} : + Continuous f ↔ + ∀ (S : Set ι) (hS : cofinite ≤ 𝓟 S), Continuous (f ∘ Pi.map fun _ ↦ inclusion _ _ hS) := by + refine ⟨by fun_prop, fun H ↦ ?_⟩ + simp_rw [continuous_iff_continuousAt, ContinuousAt] + intro x + set S : Set ι := {i | ∀ j, x j i ∈ C j i} + have hS : cofinite ≤ 𝓟 S := by + rw [le_principal_iff] + change ∀ᶠ i in cofinite, ∀ j : n, x j i ∈ C j i + simp [- eventually_cofinite] + let x' (j : n) : Πʳ i : ι, [A j i, C j i]_[𝓟 S] := .mk (fun i ↦ x j i) (fun i hi ↦ hi _) + have hxx' : Pi.map (fun j ↦ inclusion _ _ hS) x' = x := rfl + simp_rw [← hxx', nhds_pi, Pi.map_apply, nhds_eq_map_inclusion (hCopen _), ← map_piMap_pi_finite, + tendsto_map'_iff, ← nhds_pi] + exact (H _ _).tendsto _ + +end cofinite + +end Topology + +section Compatibility +/-! +## Compatibility properties between algebra and topology +-/ + +variable {S : ι → Type*} -- subobject type +variable [Π i, SetLike (S i) (R i)] +variable {B : Π i, S i} +variable {T : Set ι} {𝓕 : Filter ι} +variable [Π i, TopologicalSpace (R i)] + +section general + +@[to_additive] +instance [Π i, Inv (R i)] [∀ i, InvMemClass (S i) (R i)] [∀ i, ContinuousInv (R i)] : + ContinuousInv (Πʳ i, [R i, B i]_[𝓕]) where + continuous_inv := by + rw [continuous_dom] + intro T hT + haveI : ContinuousInv (Πʳ i, [R i, B i]_[𝓟 T]) := + isEmbedding_coe_of_principal.continuousInv fun _ ↦ rfl + exact (continuous_inclusion hT).comp continuous_inv + +@[to_additive] +instance {G : Type*} [Π i, SMul G (R i)] [∀ i, SMulMemClass (S i) G (R i)] + [∀ i, ContinuousConstSMul G (R i)] : + ContinuousConstSMul G (Πʳ i, [R i, B i]_[𝓕]) where + continuous_const_smul g := by + rw [continuous_dom] + intro T hT + haveI : ContinuousConstSMul G (Πʳ i, [R i, B i]_[𝓟 T]) := + isEmbedding_coe_of_principal.continuousConstSMul id rfl + exact (continuous_inclusion hT).comp (continuous_const_smul g) + +end general + +section principal + +@[to_additive] +instance [Π i, Mul (R i)] [∀ i, MulMemClass (S i) (R i)] [∀ i, ContinuousMul (R i)] : + ContinuousMul (Πʳ i, [R i, B i]_[𝓟 T]) := + let φ : Πʳ i, [R i, B i]_[𝓟 T] →ₙ* Π i, R i := + { toFun := (↑) + map_mul' := fun _ _ ↦ rfl } + isEmbedding_coe_of_principal.continuousMul φ + +@[to_additive] +instance {G : Type*} [TopologicalSpace G] [Π i, SMul G (R i)] [∀ i, SMulMemClass (S i) G (R i)] + [∀ i, ContinuousSMul G (R i)] : + ContinuousSMul G (Πʳ i, [R i, B i]_[𝓟 T]) := + isEmbedding_coe_of_principal.continuousSMul continuous_id rfl + +@[to_additive] +instance [Π i, Group (R i)] [∀ i, SubgroupClass (S i) (R i)] [∀ i, IsTopologicalGroup (R i)] : + IsTopologicalGroup (Πʳ i, [R i, B i]_[𝓟 T]) where + +instance [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] [∀ i, IsTopologicalRing (R i)] : + IsTopologicalRing (Πʳ i, [R i, B i]_[𝓟 T]) where + +end principal + +section cofinite + +theorem nhds_zero_eq_map_ofPre [Π i, Zero (R i)] [∀ i, ZeroMemClass (S i) (R i)] + (hBopen : ∀ i, IsOpen (B i : Set (R i))) (hT : cofinite ≤ 𝓟 T) : + (𝓝 (inclusion R (fun i ↦ B i) hT 0)) = .map (inclusion R (fun i ↦ B i) hT) (𝓝 0) := + nhds_eq_map_inclusion hBopen hT 0 + +theorem nhds_zero_eq_map_structureMap [Π i, Zero (R i)] [∀ i, ZeroMemClass (S i) (R i)] + (hBopen : ∀ i, IsOpen (B i : Set (R i))) : + (𝓝 (structureMap R (fun i ↦ B i) cofinite 0)) = + .map (structureMap R (fun i ↦ B i) cofinite) (𝓝 0) := + nhds_eq_map_structureMap hBopen 0 + +-- TODO: Make `IsOpen` a class like `IsClosed` ? +variable [hBopen : Fact (∀ i, IsOpen (B i : Set (R i)))] + +@[to_additive] +instance [Π i, Mul (R i)] [∀ i, MulMemClass (S i) (R i)] [∀ i, ContinuousMul (R i)] : + ContinuousMul (Πʳ i, [R i, B i]) where + continuous_mul := by + rw [continuous_dom_prod hBopen.out hBopen.out] + exact fun S hS ↦ (continuous_inclusion hS).comp continuous_mul + +@[to_additive] +instance continuousSMul {G : Type*} [TopologicalSpace G] [Π i, SMul G (R i)] + [∀ i, SMulMemClass (S i) G (R i)] [∀ i, ContinuousSMul G (R i)] : + ContinuousSMul G (Πʳ i, [R i, B i]) where + continuous_smul := by + rw [continuous_dom_prod_left hBopen.out] + exact fun S hS ↦ (continuous_inclusion hS).comp continuous_smul + +@[to_additive] +instance isTopologicalGroup [Π i, Group (R i)] [∀ i, SubgroupClass (S i) (R i)] + [∀ i, IsTopologicalGroup (R i)] : + IsTopologicalGroup (Πʳ i, [R i, B i]) where + +instance isTopologicalRing [Π i, Ring (R i)] [∀ i, SubringClass (S i) (R i)] + [∀ i, IsTopologicalRing (R i)] : + IsTopologicalRing (Πʳ i, [R i, B i]) where + +/-- Assume that each `R i` is a locally compact group with `A i` an open subgroup. +Assume also that all but finitely many `A i`s are compact. +Then the restricted product `Πʳ i, [R i, A i]` is a locally compact group. -/ +@[to_additive +"Assume that each `R i` is a locally compact additive group with `A i` an open subgroup. +Assume also that all but finitely many `A i`s are compact. +Then the restricted product `Πʳ i, [R i, A i]` is a locally compact additive group."] +theorem locallyCompactSpace_of_group [Π i, Group (R i)] [∀ i, SubgroupClass (S i) (R i)] + [∀ i, IsTopologicalGroup (R i)] [∀ i, LocallyCompactSpace (R i)] + (hBcompact : ∀ᶠ i in cofinite, IsCompact (B i : Set (R i))) : + LocallyCompactSpace (Πʳ i, [R i, B i]) := + haveI : WeaklyLocallyCompactSpace (Πʳ i, [R i, B i]) := + weaklyLocallyCompactSpace_of_cofinite hBopen.out hBcompact + inferInstance + +open Pointwise in +@[to_additive] +instance [Π i, Group (R i)] [∀ i, SubgroupClass (S i) (R i)] [∀ i, IsTopologicalGroup (R i)] + [hAcompact : ∀ i, CompactSpace (B i)] : LocallyCompactSpace (Πʳ i, [R i, B i]) := + -- TODO: extract as a lemma + haveI : ∀ i, WeaklyLocallyCompactSpace (R i) := fun i ↦ .mk fun x ↦ + ⟨x • (B i : Set (R i)), .smul _ (isCompact_iff_compactSpace.mpr inferInstance), + hBopen.out i |>.smul _ |>.mem_nhds <| by + simpa using smul_mem_smul_set (a := x) (one_mem (B i))⟩ + locallyCompactSpace_of_group _ <| .of_forall fun _ ↦ isCompact_iff_compactSpace.mpr inferInstance + +end cofinite + +end Compatibility + +section map_continuous + +variable {ι₁ ι₂ : Type*} +variable (R₁ : ι₁ → Type*) (R₂ : ι₂ → Type*) +variable [∀ i, TopologicalSpace (R₁ i)] [∀ i, TopologicalSpace (R₂ i)] +variable {𝓕₁ : Filter ι₁} {𝓕₂ : Filter ι₂} +variable {A₁ : (i : ι₁) → Set (R₁ i)} {A₂ : (i : ι₂) → Set (R₂ i)} +variable (f : ι₂ → ι₁) (hf : Tendsto f 𝓕₂ 𝓕₁) + +variable (φ : ∀ j, R₁ (f j) → R₂ j) (hφ : ∀ᶠ j in 𝓕₂, MapsTo (φ j) (A₁ (f j)) (A₂ j)) + +theorem map_continuous (φ_cont : ∀ j, Continuous (φ j)) : Continuous (map R₁ R₂ f hf φ hφ) := by + rw [continuous_dom] + intro S hS + set T := f ⁻¹' S ∩ {j | MapsTo (φ j) (A₁ (f j)) (A₂ j)} + have hT : 𝓕₂ ≤ 𝓟 T := by + rw [le_principal_iff] at hS ⊢ + exact inter_mem (hf hS) hφ + have hf' : Tendsto f (𝓟 T) (𝓟 S) := by aesop + have hφ' : ∀ᶠ j in 𝓟 T, MapsTo (φ j) (A₁ (f j)) (A₂ j) := by aesop + have key : map R₁ R₂ f hf φ hφ ∘ inclusion R₁ A₁ hS = + inclusion R₂ A₂ hT ∘ map R₁ R₂ f hf' φ hφ' := rfl + rw [key] + exact continuous_inclusion _ |>.comp <| + continuous_rng_of_principal.mpr <| + continuous_pi fun j ↦ φ_cont j |>.comp <| continuous_eval (f j) + +end map_continuous + +end RestrictedProduct diff --git a/Mathlib/Topology/Category/CompHausLike/Basic.lean b/Mathlib/Topology/Category/CompHausLike/Basic.lean index a7d156097db4e8..6d595a5ece6785 100644 --- a/Mathlib/Topology/Category/CompHausLike/Basic.lean +++ b/Mathlib/Topology/Category/CompHausLike/Basic.lean @@ -258,8 +258,6 @@ of topological spaces. -/ def isoEquivHomeo {X Y : CompHausLike.{u} P} : (X ≅ Y) ≃ (X ≃ₜ Y) where toFun := homeoOfIso invFun := isoOfHomeo - left_inv _ := rfl - right_inv _ := rfl /-- A constant map as a morphism in `CompHausLike` -/ def const {P : TopCat.{u} → Prop} diff --git a/Mathlib/Topology/Category/CompactlyGenerated.lean b/Mathlib/Topology/Category/CompactlyGenerated.lean index a1d5c5cb870c6e..5ef8e6099aa939 100644 --- a/Mathlib/Topology/Category/CompactlyGenerated.lean +++ b/Mathlib/Topology/Category/CompactlyGenerated.lean @@ -105,11 +105,5 @@ of topological spaces. -/ def isoEquivHomeo {X Y : CompactlyGenerated.{u, w}} : (X ≅ Y) ≃ (X ≃ₜ Y) where toFun := homeoOfIso invFun := isoOfHomeo - left_inv f := by - ext - rfl - right_inv f := by - ext - rfl end CompactlyGenerated diff --git a/Mathlib/Topology/Category/Profinite/Basic.lean b/Mathlib/Topology/Category/Profinite/Basic.lean index 30df034906efa8..1614402158bacd 100644 --- a/Mathlib/Topology/Category/Profinite/Basic.lean +++ b/Mathlib/Topology/Category/Profinite/Basic.lean @@ -105,7 +105,6 @@ def Profinite.toCompHausEquivalence (X : CompHaus.{u}) (Y : Profinite.{u}) : { toFun := Continuous.connectedComponentsLift g.hom.2 continuous_toFun := Continuous.connectedComponentsLift_continuous g.hom.2 } left_inv _ := TopCat.ext <| ConnectedComponents.surjective_coe.forall.2 fun _ => rfl - right_inv _ := TopCat.ext fun _ => rfl /-- The connected_components functor from compact Hausdorff spaces to profinite spaces, left adjoint to the inclusion functor. diff --git a/Mathlib/Topology/Category/Sequential.lean b/Mathlib/Topology/Category/Sequential.lean index bf7f8faa77eb7e..1dfb1295ccbd51 100644 --- a/Mathlib/Topology/Category/Sequential.lean +++ b/Mathlib/Topology/Category/Sequential.lean @@ -93,11 +93,5 @@ of topological spaces. -/ def isoEquivHomeo {X Y : Sequential.{u}} : (X ≅ Y) ≃ (X ≃ₜ Y) where toFun := homeoOfIso invFun := isoOfHomeo - left_inv f := by - ext - rfl - right_inv f := by - ext - rfl end Sequential diff --git a/Mathlib/Topology/Category/TopCat/Limits/Pullbacks.lean b/Mathlib/Topology/Category/TopCat/Limits/Pullbacks.lean index 6046e580c381b8..0336ec962d93ec 100644 --- a/Mathlib/Topology/Category/TopCat/Limits/Pullbacks.lean +++ b/Mathlib/Topology/Category/TopCat/Limits/Pullbacks.lean @@ -154,7 +154,6 @@ def pullbackHomeoPreimage apply hg.injective convert x.prop exact Exists.choose_spec (p := fun y ↦ g y = f (↑x : X × Y).1) _ - right_inv := fun _ ↦ rfl continuous_toFun := by fun_prop continuous_invFun := by apply Continuous.subtype_mk @@ -342,9 +341,7 @@ theorem fst_iso_of_right_embedding_range_subset {X Y S : TopCat} (f : X ⟶ S) { invFun := fun x => ⟨x, by rw [pullback_fst_range] - exact ⟨_, (H (Set.mem_range_self x)).choose_spec.symm⟩⟩ - left_inv := fun ⟨_, _⟩ => rfl - right_inv := fun x => rfl } + exact ⟨_, (H (Set.mem_range_self x)).choose_spec.symm⟩⟩ } convert (isoOfHomeo esto).isIso_hom theorem snd_iso_of_left_embedding_range_subset {X Y S : TopCat} {f : X ⟶ S} (hf : IsEmbedding f) @@ -355,9 +352,7 @@ theorem snd_iso_of_left_embedding_range_subset {X Y S : TopCat} {f : X ⟶ S} (h invFun := fun x => ⟨x, by rw [pullback_snd_range] - exact ⟨_, (H (Set.mem_range_self x)).choose_spec⟩⟩ - left_inv := fun ⟨_, _⟩ => rfl - right_inv := fun x => rfl } + exact ⟨_, (H (Set.mem_range_self x)).choose_spec⟩⟩ } convert (isoOfHomeo esto).isIso_hom theorem pullback_snd_image_fst_preimage (f : X ⟶ Z) (g : Y ⟶ Z) (U : Set X) : diff --git a/Mathlib/Topology/CompactOpen.lean b/Mathlib/Topology/CompactOpen.lean index 9be918d937c5a2..3653d5ce78e400 100644 --- a/Mathlib/Topology/CompactOpen.lean +++ b/Mathlib/Topology/CompactOpen.lean @@ -476,7 +476,6 @@ def curry [LocallyCompactSpace X] [LocallyCompactSpace Y] : C(X × Y, Z) ≃ₜ def continuousMapOfUnique [Unique X] : Y ≃ₜ C(X, Y) where toFun := const X invFun f := f default - left_inv _ := rfl right_inv f := by ext x rw [Unique.eq_default x] diff --git a/Mathlib/Topology/Compactification/OnePoint/Basic.lean b/Mathlib/Topology/Compactification/OnePoint/Basic.lean index d5f9fb250d6f47..ea77de9303e9ba 100644 --- a/Mathlib/Topology/Compactification/OnePoint/Basic.lean +++ b/Mathlib/Topology/Compactification/OnePoint/Basic.lean @@ -435,7 +435,6 @@ noncomputable def continuousMapDiscreteEquiv (Y : Type*) [DiscreteTopology X] [T ⟨fun x ↦ f x, ⟨f ∞, continuous_iff_from_discrete f |>.mp <| map_continuous f⟩⟩ exact Classical.choose_spec f'.property · simp - right_inv _ := rfl lemma continuous_iff_from_nat {Y : Type*} [TopologicalSpace Y] (f : OnePoint ℕ → Y) : Continuous f ↔ Tendsto (fun x : ℕ ↦ f x) atTop (𝓝 (f ∞)) := by @@ -458,9 +457,7 @@ noncomputable def continuousMapNatEquiv (Y : Type*) [TopologicalSpace Y] [T2Spac C(OnePoint ℕ, Y) ≃ { f : ℕ → Y // ∃ L, Tendsto (f ·) atTop (𝓝 L) } := by refine (continuousMapDiscreteEquiv ℕ Y).trans { toFun := fun ⟨f, hf⟩ ↦ ⟨f, by rwa [← Nat.cofinite_eq_atTop]⟩ - invFun := fun ⟨f, hf⟩ ↦ ⟨f, by rwa [Nat.cofinite_eq_atTop]⟩ - left_inv := fun _ ↦ rfl - right_inv := fun _ ↦ rfl } + invFun := fun ⟨f, hf⟩ ↦ ⟨f, by rwa [Nat.cofinite_eq_atTop]⟩ } /-- If `X` is not a compact space, then the natural embedding `X → OnePoint X` has dense range. -/ diff --git a/Mathlib/Topology/Compactness/Compact.lean b/Mathlib/Topology/Compactness/Compact.lean index a45acf4b689fa0..e289c4a9acd35c 100644 --- a/Mathlib/Topology/Compactness/Compact.lean +++ b/Mathlib/Topology/Compactness/Compact.lean @@ -994,6 +994,26 @@ instance {X : ι → Type*} [Finite ι] [∀ i, TopologicalSpace (X i)] [∀ i, rw [Sigma.univ] exact isCompact_iUnion fun i => isCompact_range continuous_sigmaMk +lemma Set.isCompact_sigma {X : ι → Type*} [∀ i, TopologicalSpace (X i)] {s : Set ι} + {t : ∀ i, Set (X i)} (hs : s.Finite) (ht : ∀ i ∈ s, IsCompact (t i)) : + IsCompact (s.sigma t) := by + rw [Set.sigma_eq_biUnion] + exact hs.isCompact_biUnion fun i hi ↦ (ht i hi).image continuous_sigmaMk + +lemma IsCompact.sigma_exists_finite_sigma_eq {X : ι → Type*} [∀ i, TopologicalSpace (X i)] + (u : Set (Σ i, X i)) (hu : IsCompact u) : + ∃ (s : Set ι) (t : ∀ i, Set (X i)), s.Finite ∧ (∀ i, IsCompact (t i)) ∧ s.sigma t = u := by + obtain ⟨s, hs⟩ := hu.elim_finite_subcover (fun i : ι ↦ Sigma.mk i '' (Sigma.mk i ⁻¹' Set.univ)) + (fun i ↦ isOpenMap_sigmaMk _ <| isOpen_univ.preimage continuous_sigmaMk) + fun x hx ↦ (by aesop) + use s, fun i ↦ Sigma.mk i ⁻¹' u, s.finite_toSet, fun i ↦ ?_, ?_ + · exact Topology.IsClosedEmbedding.sigmaMk.isCompact_preimage hu + · ext x + simp only [Set.mem_sigma_iff, Finset.mem_coe, Set.mem_preimage, and_iff_right_iff_imp] + intro hx + obtain ⟨i, hi⟩ := Set.mem_iUnion.mp (hs hx) + aesop + /-- The coproduct of the cocompact filters on two topological spaces is the cocompact filter on their product. -/ theorem Filter.coprod_cocompact : diff --git a/Mathlib/Topology/Compactness/CompactSystem.lean b/Mathlib/Topology/Compactness/CompactSystem.lean new file mode 100644 index 00000000000000..d84700594ff1ab --- /dev/null +++ b/Mathlib/Topology/Compactness/CompactSystem.lean @@ -0,0 +1,381 @@ +/- +Copyright (c) 2025 Peter Pfaffelhuber. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Rémy Degenne, Peter Pfaffelhuber +-/ +import Mathlib.Data.Set.Dissipate +import Mathlib.Logic.IsEmpty +import Mathlib.MeasureTheory.Constructions.Cylinders +import Mathlib.Order.OmegaCompletePartialOrder +import Mathlib.Topology.Separation.Hausdorff +/-! +# Compact systems. + +This file defines compact systems of sets. + +## Main definitions + +* `IsCompactSystem`: A set of sets is a compact system if, whenever a countable subfamily has empty + intersection, then finitely many of them already have empty intersection. + +## Main results + +* `IsCompactSystemiff_isCompactSystem_of_or_univ`: A set system is a compact +system iff inserting `univ` gives a compact system. +* `IsClosedCompact.isCompactSystem`: The set of closed and compact sets is a compact system. +* `IsClosedCompact.isCompactSystem_of_T2Space`: In a `T2Space α`, the set of compact sets + is a compact system in a `T2Space`. +* `IsCompactSystem.closedCompactSquareCylinders`: Closed and compact square cylinders form a + compact system. +-/ + +open Set Nat MeasureTheory + +variable {α : Type*} {p : Set α → Prop} {C : ℕ → Set α} + +section definition + +/-- A set of sets is a compact system if, whenever a countable subfamily has empty intersection, +then finitely many of them already have empty intersection. -/ +def IsCompactSystem (p : Set α → Prop) : Prop := + ∀ C : ℕ → Set α, (∀ i, p (C i)) → ⋂ i, C i = ∅ → ∃ (n : ℕ), Dissipate C n = ∅ + +end definition + +namespace IsCompactSystem + +open Classical in +/-- In a compact system, given a countable family with `⋂ i, C i = ∅`, we choose the smallest `n` +with `⋂ (i ≤ n), C i = ∅`. -/ +noncomputable +def finite_of_empty (hp : IsCompactSystem p) (hC : ∀ i, p (C i)) + (hC_empty : ⋂ i, C i = ∅) : ℕ := + Nat.find (hp C hC hC_empty) + +open Classical in +lemma dissipate_eq_empty (hp : IsCompactSystem p) (hC : ∀ i, p (C i)) + (hC_empty : ⋂ i, C i = ∅) : + Dissipate C (hp.finite_of_empty hC hC_empty) = ∅ := by + apply Nat.find_spec (hp C hC hC_empty) + +theorem iff_nonempty_iInter (p : Set α → Prop) : + IsCompactSystem p ↔ (∀ C : ℕ → Set α, (∀ i, p (C i)) → (∀ (n : ℕ), + (Dissipate C n).Nonempty) → (⋂ i, C i).Nonempty) := by + refine ⟨fun h C hC hn ↦ ?_, fun h C hC ↦ ?_⟩ <;> have h2 := not_imp_not.mpr <| h C hC + · push_neg at h2 + exact h2 hn + · push_neg at h2 + exact h2 + +/-- In this equivalent formulation for a compact system, +note that we use `⋂ k < n, C k` rather than `⋂ k ≤ n, C k`. -/ +lemma iff_nonempty_iInter_of_lt (p : Set α → Prop) : IsCompactSystem p ↔ + ∀ C : ℕ → Set α, (∀ i, p (C i)) → (∀ n, (⋂ k < n, C k).Nonempty) → (⋂ i, C i).Nonempty := by + simp_rw [iff_nonempty_iInter] + refine ⟨fun h C hi h'↦ ?_, fun h C hi h' ↦ ?_⟩ + · apply h C hi + exact fun n ↦ dissipate_eq ▸ (h' (n + 1)) + · apply h C hi + intro n + simp_rw [Set.nonempty_iff_ne_empty] at h' ⊢ + intro g + apply h' n + simp_rw [← subset_empty_iff, Dissipate] at g ⊢ + apply le_trans _ g + intro x + rw [mem_iInter₂, mem_iInter₂] + exact fun h i hi ↦ h i hi.le + +/-- Any subset of a compact system is a compact system. -/ +theorem mono {C D : (Set α) → Prop} (hD : IsCompactSystem D) (hCD : ∀ s, C s → D s) : + IsCompactSystem C := fun s hC hs ↦ hD s (fun i ↦ hCD (s i) (hC i)) hs + +/-- A set system is a compact system iff adding `∅` gives a compact system. -/ +lemma iff_isCompactSystem_of_or_empty : IsCompactSystem p ↔ + IsCompactSystem (fun s ↦ (p s ∨ (s = ∅))) := by + refine ⟨fun h s h' hd ↦ ?_, fun h ↦ mono h (fun s ↦ fun a ↦ Or.symm (Or.inr a))⟩ + by_cases g : ∃ n, s n = ∅ + · use g.choose + rw [← subset_empty_iff] at hd ⊢ + exact le_trans (dissipate_subset (by rfl)) g.choose_spec.le + · push_neg at g + have hj (i : _) : p (s i) := by + rcases h' i with a | b + · exact a + · exfalso + revert g i + simp_rw [← Set.not_nonempty_iff_eq_empty] + simp_rw [imp_false, not_not] + exact fun h i ↦ h i + exact h s hj hd + +lemma of_IsEmpty (h : IsEmpty α) (p : Set α → Prop) : IsCompactSystem p := + fun s _ _ ↦ ⟨0, Set.eq_empty_of_isEmpty (Dissipate s 0)⟩ + +/-- A set system is a compact system iff adding `univ` gives a compact system. -/ +lemma iff_isCompactSystem_of_or_univ : IsCompactSystem p ↔ + IsCompactSystem (fun s ↦ (p s ∨ s = univ)) := by + refine ⟨fun h ↦ ?_, fun h ↦ mono h (fun s ↦ fun a ↦ Or.symm (Or.inr a))⟩ + wlog ht : Nonempty α + · rw [not_nonempty_iff] at ht + apply of_IsEmpty ht + · rw [iff_nonempty_iInter] at h ⊢ + intro s h' hd + classical + by_cases h₀ : ∀ n, ¬p (s n) + · simp only [h₀, false_or] at h' + simp_rw [h', iInter_univ, Set.univ_nonempty] + · push_neg at h₀ + let n := Nat.find h₀ + let s' := fun i ↦ if p (s i) then s i else s n + have h₁ : ∀ i, p (s' i) := by + intro i + by_cases h₁ : p (s i) + · simp only [h₁, ↓reduceIte, s'] + · simp only [h₁, ↓reduceIte, Nat.find_spec h₀, s', n] + have h₃ : ∀ i, (p (s i) → s' i = s i) := fun i h ↦ if_pos h + have h₄ : ∀ i, (¬p (s i) → s' i = s n) := fun i h ↦ if_neg h + have h₂ : ⋂ i, s i = ⋂ i, s' i := by + simp only [s'] at * + ext x + simp only [mem_iInter] + refine ⟨fun h i ↦ ?_, fun h i ↦ ?_⟩ + · by_cases h' : p (s i) <;> simp only [h', ↓reduceIte, h, s', n] + · specialize h' i + specialize h i + rcases h' with a | b + · simp only [a, ↓reduceIte, s', n] at h + exact h + · simp only [b, Set.mem_univ] + apply h₂ ▸ h s' h₁ + by_contra! a + obtain ⟨j, hj⟩ := a + have h₂ (v : ℕ) (hv : n ≤ v) : Dissipate s v = Dissipate s' v:= by + ext x + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ <;> simp only [dissipate_def, mem_iInter] at h ⊢ <;> + intro i hi + · by_cases h₅ : p (s i) + · exact (h₃ i h₅) ▸ h i hi + · exact (h₄ i h₅) ▸ h n hv + · by_cases h₅ : p (s i) + · exact (h₃ i h₅) ▸ h i hi + · have h₆ : s i = univ := by + specialize h' i + simp only [h₅, false_or] at h' + exact h' + simp only [h₆, Set.mem_univ] + have h₇ : Dissipate s' (max j n) = ∅ := by + rw [← subset_empty_iff] at hj ⊢ + exact le_trans (dissipate_antitone (Nat.le_max_left j n)) hj + specialize h₂ (max j n) (Nat.le_max_right j n) + specialize hd (max j n) + rw [h₂, Set.nonempty_iff_ne_empty, h₇] at hd + exact hd rfl + +theorem iff_directed (hpi : IsPiSystem p) : + IsCompactSystem p ↔ + ∀ (C : ℕ → Set α), ∀ (_ : Directed (fun (x1 x2 : Set α) => x1 ⊇ x2) C), (∀ i, p (C i)) → + ⋂ i, C i = ∅ → ∃ (n : ℕ), C n = ∅ := by + rw [iff_isCompactSystem_of_or_empty] + refine ⟨fun h ↦ fun C hdi hi ↦ ?_, fun h C h1 h2 ↦ ?_⟩ + · rw [exists_dissipate_eq_empty_iff_of_directed C hdi] + apply h C + exact fun i ↦ Or.inl (hi i) + · have hpi' : IsPiSystem (fun s ↦ p s ∨ s = ∅) := by + intro a ha b hb hab + rcases ha with ha₁ | ha₂ + · rcases hb with hb₁ | hb₂ + · left + exact hpi a ha₁ b hb₁ hab + · right + exact hb₂ ▸ (Set.inter_empty a) + · simp only [ha₂, Set.empty_inter] + right + rfl + rw [← biInter_le_eq_iInter] at h2 + obtain h' := h (Dissipate C) directed_dissipate + have h₀ : (∀ (n : ℕ), p (Dissipate C n) ∨ Dissipate C n = ∅) → ⋂ n, Dissipate C n = ∅ → + ∃ n, Dissipate C n = ∅ := by + intro h₀ h₁ + by_cases f : ∀ n, p (Dissipate C n) + · apply h' f h₁ + · push_neg at f + obtain ⟨n, hn⟩ := f + use n + specialize h₀ n + simp_all only [false_or] + obtain h'' := dissipate_of_piSystem hpi' h1 + have h₁ : ∀ (n : ℕ), p (Dissipate C n) ∨ Dissipate C n = ∅ := by + intro n + by_cases g : (Dissipate C n).Nonempty + · exact h'' n g + · right + exact Set.not_nonempty_iff_eq_empty.mp g + apply h₀ h₁ h2 + +theorem iff_directed' (hpi : IsPiSystem p) : + IsCompactSystem p ↔ + ∀ (C : ℕ → Set α), ∀ (_ : Directed (fun (x1 x2 : Set α) => x1 ⊇ x2) C), (∀ i, p (C i)) → + (∀ (n : ℕ), (C n).Nonempty) → (⋂ i, C i).Nonempty := by + rw [IsCompactSystem.iff_directed hpi] + refine ⟨fun h1 C h3 h4 ↦ ?_, fun h1 C h3 s ↦ ?_⟩ <;> rw [← not_imp_not] <;> push_neg + · exact h1 C h3 h4 + · exact h1 C h3 s + +section IsCompactIsClosed + +variable {α : Type*} [TopologicalSpace α] + +/-- The set of compact and closed sets is a compact system. -/ +theorem of_isCompact_isClosed : + IsCompactSystem (fun s : Set α ↦ IsCompact s ∧ IsClosed s) := by + let p := fun (s : Set α) ↦ IsCompact s ∧ IsClosed s + have h2 : IsPiSystem p := by + intro s hs t ht _ + refine ⟨IsCompact.inter_left ht.1 hs.2, IsClosed.inter hs.2 ht.2⟩ + rw [IsCompactSystem.iff_directed' h2] + intro s hs h1 h2 + let s' := fun (i : { j : ℕ | s j ≠ univ}) ↦ s i + have hs' : Directed (fun x1 x2 ↦ x1 ⊇ x2) s' := by + intro a b + obtain ⟨z, hz1, hz2⟩ := hs a.val b.val + have hz : s z ≠ univ := fun h ↦ a.prop <| eq_univ_of_subset hz1 h + use ⟨z, hz⟩ + have htcl : ∀ (i : { j : ℕ | s j ≠ univ}), IsClosed (s i) := + fun i ↦ (h1 i).2 + have htco : ∀ (i : { j : ℕ | s j ≠ univ}), IsCompact (s i) := + fun i ↦ (h1 i).1 + haveI f : Nonempty α := by + apply Exists.nonempty _ + · exact fun x ↦ x ∈ s 0 + · exact h2 0 + by_cases h : Nonempty ↑{j | s j ≠ Set.univ} + · have g : (⋂ i, s' i).Nonempty → (⋂ i, s i).Nonempty := by + rw [Set.nonempty_iInter, Set.nonempty_iInter] + rintro ⟨x, hx⟩ + use x + intro i + by_cases g : s i ≠ univ + · exact hx ⟨i, g⟩ + · simp only [ne_eq, not_not, s'] at g + rw [g] + simp only [Set.mem_univ] + apply g <| IsCompact.nonempty_iInter_of_directed_nonempty_isCompact_isClosed s' hs' + (fun j ↦ h2 j) htco htcl + · simp only [ne_eq, coe_setOf, nonempty_subtype, not_exists, not_not, s'] at h + simp [s', h] + +theorem nonempty_isCompactIsClosed : Nonempty { t : Set α | IsCompact t ∧ IsClosed t } := by + simp only [coe_setOf, nonempty_subtype] + use ∅ + simp + +/-- The set of sets which are either compact and closed, or `univ`, is a compact system. -/ +theorem of_isCompact_isClosed_or_univ : + IsCompactSystem (fun s : Set α ↦ (IsCompact s ∧ IsClosed s) ∨ (s = univ)) := by + rw [← iff_isCompactSystem_of_or_univ] + exact of_isCompact_isClosed + +/-- In a `T2Space` the set of compact sets is a compact system. -/ +theorem of_isCompact [T2Space α] : + IsCompactSystem (fun s : Set α ↦ IsCompact s) := by + have h : (fun s : Set α ↦ IsCompact s) = (fun s : Set α ↦ IsCompact s ∧ IsClosed s) := by + ext s + refine ⟨fun h' ↦ ⟨h', h'.isClosed⟩, fun h ↦ h.1⟩ + exact h ▸ (of_isCompact_isClosed) + +end IsCompactIsClosed + +section pi + +variable {ι : Type*} {α : ι → Type*} + +/- In a product space, the intersection of square cylinders is empty iff there is a coordinate `i` +such that the projections to `i` have empty intersection. -/ +theorem iInter_pi_empty_iff {β : Type*} (s : β → Set ι) (t : β → (i : ι) → Set (α i)) : + (⋂ b, ((s b).pi (t b)) = ∅) ↔ (∃ i : ι, ⋂ (b : β) (_: i ∈ s b), (t b i) = ∅):= by + rw [iInter_eq_empty_iff, not_iff_not.symm] + push_neg + simp only [nonempty_iInter, mem_iInter] + refine ⟨fun ⟨x, hx⟩ i ↦ ?_, fun h ↦ ?_⟩ + · refine ⟨x i, fun j hi ↦ hx j i hi⟩ + · choose x hx using h + refine ⟨x, fun i j hj ↦ hx j i hj⟩ + +theorem iInter_univ_pi_empty_iff {β : Type*} (t : β → (i : ι) → Set (α i)) : + ( ⋂ b, (univ.pi (t b)) = ∅) ↔ (∃ i : ι, ⋂ (b : β), (t b i) = ∅):= by + rw [iInter_pi_empty_iff] + simp only [mem_univ, iInter_true] + +theorem biInter_univ_pi_empty_iff {β : Type*} (t : β → (i : ι) → Set (α i)) (p : β → Prop): + ( ⋂ (b : β), ⋂ (_ : p b), (univ.pi (t b)) = ∅) ↔ + (∃ i : ι, ⋂ (b : β), ⋂ (_ : p b), (t b i) = ∅) := by + have h : ⋂ (b : β), ⋂ (_ : p b), (univ.pi (t b)) = + ⋂ (b : { (b' : β) | p b' }), (univ.pi (t b.val)) := by + exact biInter_eq_iInter p fun x h ↦ univ.pi (t x) + have h' (i : ι) : ⋂ (b : β), ⋂ (_ : p b), t b i = ⋂ (b : { (b' : β) | p b' }), t b.val i := by + exact biInter_eq_iInter p fun x h ↦ t x i + simp_rw [h, h', iInter_univ_pi_empty_iff] + +theorem pi (C : (i : ι) → Set (Set (α i))) (hC : ∀ i, IsCompactSystem (C i)) : + IsCompactSystem (univ.pi '' univ.pi C) := by + intro S hS h_empty + change ∀ i, S i ∈ univ.pi '' univ.pi C at hS + simp only [mem_image, mem_pi, mem_univ, forall_const] at hS + choose x hx1 hx2 using hS + simp_rw [← hx2] at h_empty ⊢ + simp_rw [iInter_univ_pi_empty_iff x] at h_empty + obtain ⟨i, hi⟩ := h_empty + let y := (fun b ↦ x b i) + have hy (b : ℕ) : y b ∈ C i := by + simp only [y] + exact hx1 b i + have ⟨n, hn⟩ := (hC i) y hy hi + use n + simp_rw [Dissipate, ← hx2] at hn ⊢ + rw [biInter_univ_pi_empty_iff x] + use i + +theorem squareCylinders (C : (i : ι) → Set (Set (α i))) (hC₀ : ∀ i, IsCompactSystem (C i)) + (hC₁ : ∀ i, Nonempty (C i)) : + IsCompactSystem (squareCylinders C) := by + apply IsCompactSystem.mono (pi _ (fun i ↦ iff_isCompactSystem_of_or_univ.mp (hC₀ i))) + intro S hS + apply squareCylinders_subset_pi _ (fun i ↦ Or.inr rfl) + change S ∈ MeasureTheory.squareCylinders C at hS + rw [mem_squareCylinders C hC₁] at hS + rw [mem_squareCylinders (fun i s ↦ C i s ∨ s = univ) + (fun i ↦ nonempty_subtype.mpr ⟨univ, Or.inr rfl⟩)] + obtain ⟨s, t, h₀, h₁⟩ := hS + use s, t + simp only [exists_prop] + exact ⟨fun i hi ↦ Or.inl (h₀ i hi), h₁⟩ + +end pi + +end IsCompactSystem + +section ClosedCompactSquareCylinders + +variable {ι : Type*} {α : ι → Type*} + +variable [∀ i, TopologicalSpace (α i)] + +variable (α) +/-- The set of sets of the form `s.pi t`, where `s : Finset ι` and `t i` is both, +closed and compact, for all `i ∈ s`. -/ +def MeasureTheory.compactClosedSquareCylinders : Set (Set (Π i, α i)) := + MeasureTheory.squareCylinders (fun i ↦ { t : Set (α i) | IsCompact t ∧ IsClosed t }) + +/-- Products of compact and closed sets form a a compact system. -/ +theorem IsCompactSystem.compactClosedPi : + IsCompactSystem (univ.pi '' univ.pi (fun i ↦ { t : Set (α i) | IsCompact t ∧ IsClosed t })) := + IsCompactSystem.pi _ (fun _ ↦ IsCompactSystem.of_isCompact_isClosed) + +/-- Compact and closed square cylinders are a compact system. -/ +theorem isCompactSystem.compactClosedSquareCylinders : + IsCompactSystem (MeasureTheory.compactClosedSquareCylinders α) := + IsCompactSystem.squareCylinders _ (fun _ ↦ IsCompactSystem.of_isCompact_isClosed) + (fun _ ↦ IsCompactSystem.nonempty_isCompactIsClosed) + +end ClosedCompactSquareCylinders diff --git a/Mathlib/Topology/Connected/PathComponentOne.lean b/Mathlib/Topology/Connected/PathComponentOne.lean new file mode 100644 index 00000000000000..400ab6ae141740 --- /dev/null +++ b/Mathlib/Topology/Connected/PathComponentOne.lean @@ -0,0 +1,41 @@ +/- +Copyright (c) 2025 Jireh Loreaux. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jireh Loreaux +-/ +import Mathlib.Topology.Algebra.OpenSubgroup +import Mathlib.Topology.Connected.LocPathConnected + +/-! # The path component of the identity in a locally path connected topological group + +This file defines the path component of the identity is an `OpenNormalSubgroup` when the ambient +topological group is locally path connected. We place this in a separate file to avoid importing +additional algebra into the topology hierarchy. +-/ + +section PathComponentOne + +variable (G : Type*) [TopologicalSpace G] + +/-- The path component of the identity in a locally path connected topological group, +as an open normal subgroup. It is, in fact, clopen. -/ +@[to_additive (attr := simps!) +"The path component of the identity in a locally path connected additive topological group, +as an open normal additive subgroup. It is, in fact, clopen."] +def OpenNormalSubgroup.pathComponentOne [Group G] + [IsTopologicalGroup G] [LocPathConnectedSpace G] : + OpenNormalSubgroup (G) where + toSubgroup := .pathComponentOne G + isOpen' := .pathComponent 1 + isNormal' := .pathComponentOne G + +namespace OpenNormalSubgroup + +@[to_additive] +instance [Group G] [IsTopologicalGroup G] [LocPathConnectedSpace G] : + IsClosed (OpenNormalSubgroup.pathComponentOne G : Set G) := + .pathComponent 1 + +end OpenNormalSubgroup + +end PathComponentOne diff --git a/Mathlib/Topology/Connected/PathConnected.lean b/Mathlib/Topology/Connected/PathConnected.lean index 62ddb586172644..c7920c81fb3d73 100644 --- a/Mathlib/Topology/Connected/PathConnected.lean +++ b/Mathlib/Topology/Connected/PathConnected.lean @@ -77,6 +77,19 @@ theorem Joined.mul {M : Type*} [Mul M] [TopologicalSpace M] [ContinuousMul M] {a b c d : M} (hs : Joined a b) (ht : Joined c d) : Joined (a * c) (b * d) := ⟨hs.somePath.mul ht.somePath⟩ +@[to_additive] +theorem Joined.listProd {M : Type*} [MulOneClass M] [TopologicalSpace M] [ContinuousMul M] + {l l' : List M} (h : List.Forall₂ Joined l l') : + Joined l.prod l'.prod := by + induction h with + | nil => rfl + | cons h₁ _ h₂ => exact h₁.mul h₂ + +@[to_additive] +theorem Joined.inv {G : Type*} [Inv G] [TopologicalSpace G] [ContinuousInv G] + {x y : G} (h : Joined x y) : Joined x⁻¹ y⁻¹ := + ⟨h.somePath.inv⟩ + variable (X) /-- The setoid corresponding the equivalence relation of being joined by a continuous path. -/ @@ -201,6 +214,12 @@ theorem JoinedIn.mul {M : Type*} [Mul M] [TopologicalSpace M] [ContinuousMul M] JoinedIn (s * t) (a * c) (b * d) := ⟨hs.somePath.mul ht.somePath, fun t ↦ Set.mul_mem_mul (hs.somePath_mem t) (ht.somePath_mem t)⟩ +@[to_additive] +theorem JoinedIn.inv {G : Type*} [InvolutiveInv G] [TopologicalSpace G] [ContinuousInv G] + {s : Set G} {a b : G} (hs : JoinedIn s a b) : + JoinedIn s⁻¹ a⁻¹ b⁻¹ := + ⟨hs.somePath.inv, fun t ↦ Set.inv_mem_inv.mpr (hs.somePath_mem t)⟩ + /-! ### Path component -/ /-- The path component of `x` is the set of points that can be joined to `x`. -/ @@ -267,6 +286,31 @@ theorem pathComponentIn_mono {G : Set X} (h : F ⊆ G) : pathComponentIn x F ⊆ pathComponentIn x G := fun _ ⟨γ, hγ⟩ ↦ ⟨γ, fun t ↦ h (hγ t)⟩ +/-! ### Path component of the identity in a group -/ + +/-- The path component of the identity in a topological monoid, as a submonoid. -/ +@[to_additive (attr := simps) +"The path component of the identity in an additive topological monoid, as an additive submonoid."] +def Submonoid.pathComponentOne (M : Type*) [Monoid M] [TopologicalSpace M] [ContinuousMul M] : + Submonoid M where + carrier := pathComponent (1 : M) + mul_mem' {m₁ m₂} hm₁ hm₂ := by simpa using hm₁.mul hm₂ + one_mem' := mem_pathComponent_self 1 + +/-- The path component of the identity in a topological group, as a subgroup. -/ +@[to_additive (attr := simps!) +"The path component of the identity in an additive topological group, as an additive subgroup."] +def Subgroup.pathComponentOne (G : Type*) [Group G] [TopologicalSpace G] [IsTopologicalGroup G] : + Subgroup G where + toSubmonoid := .pathComponentOne G + inv_mem' {g} hg := by simpa using hg.inv + +/-- The path component of the identity in a topological group is normal. -/ +@[to_additive] +instance Subgroup.Normal.pathComponentOne (G : Type*) [Group G] [TopologicalSpace G] + [IsTopologicalGroup G] : (Subgroup.pathComponentOne G).Normal where + conj_mem _ := fun ⟨γ⟩ g ↦ ⟨⟨⟨(g * γ · * g⁻¹), by fun_prop⟩, by simp, by simp⟩⟩ + /-! ### Path connected sets -/ @@ -313,6 +357,13 @@ theorem IsPathConnected.mul {M : Type*} [Mul M] [TopologicalSpace M] [Continuous let ⟨a, ha_mem, ha⟩ := hs; let ⟨b, hb_mem, hb⟩ := ht ⟨a * b, mul_mem_mul ha_mem hb_mem, Set.forall_mem_image2.2 fun _x hx _y hy ↦ (ha hx).mul (hb hy)⟩ +@[to_additive] +theorem IsPathConnected.inv {G : Type*} [InvolutiveInv G] [TopologicalSpace G] [ContinuousInv G] + {s : Set G} (hs : IsPathConnected s) : + IsPathConnected s⁻¹ := + let ⟨a, ha_mem, ha⟩ := hs + ⟨a⁻¹, inv_mem_inv.mpr ha_mem, fun x hx ↦ by simpa using ha (mem_inv.mp hx) |>.map continuous_inv⟩ + /-- If `f : X → Y` is an inducing map, `f(F)` is path-connected iff `F` is. -/ nonrec theorem Topology.IsInducing.isPathConnected_iff {f : X → Y} (hf : IsInducing f) : IsPathConnected F ↔ IsPathConnected (f '' F) := by diff --git a/Mathlib/Topology/Constructions/SumProd.lean b/Mathlib/Topology/Constructions/SumProd.lean index 32a4e92f214c24..d1c87452dcdf24 100644 --- a/Mathlib/Topology/Constructions/SumProd.lean +++ b/Mathlib/Topology/Constructions/SumProd.lean @@ -587,6 +587,12 @@ lemma Topology.isInducing_prod_const {y : Y} {f : X → Z} : @[deprecated (since := "2024-10-28")] alias inducing_prod_const := isInducing_prod_const +lemma isInducing_prodMkLeft (y : Y) : IsInducing (fun x : X ↦ (x, y)) := + .of_comp (.prodMk_left y) continuous_fst .id + +lemma isInducing_prodMkRight (x : X) : IsInducing (Prod.mk x : Y → X × Y) := + .of_comp (.prodMk_right x) continuous_snd .id + lemma Topology.IsEmbedding.prodMap {f : X → Y} {g : Z → W} (hf : IsEmbedding f) (hg : IsEmbedding g) : IsEmbedding (Prod.map f g) where toIsInducing := hf.isInducing.prodMap hg.isInducing @@ -609,11 +615,14 @@ lemma isEmbedding_graph {f : X → Y} (hf : Continuous f) : IsEmbedding fun x => @[deprecated (since := "2024-10-26")] alias embedding_graph := isEmbedding_graph -lemma isEmbedding_prodMk (x : X) : IsEmbedding (Prod.mk x : Y → X × Y) := +lemma isEmbedding_prodMkLeft (y : Y) : IsEmbedding (fun x : X ↦ (x, y)) := + .of_comp (.prodMk_left y) continuous_fst .id + +lemma isEmbedding_prodMkRight (x : X) : IsEmbedding (Prod.mk x : Y → X × Y) := .of_comp (.prodMk_right x) continuous_snd .id -@[deprecated (since := "2024-10-26")] -alias embedding_prod_mk := isEmbedding_prodMk +@[deprecated (since := "2025-06-12")] alias isEmbedding_prodMk := isEmbedding_prodMkRight +@[deprecated (since := "2024-10-26")] alias embedding_prod_mk := isEmbedding_prodMkRight theorem IsOpenQuotientMap.prodMap {f : X → Y} {g : Z → W} (hf : IsOpenQuotientMap f) (hg : IsOpenQuotientMap g) : IsOpenQuotientMap (Prod.map f g) := diff --git a/Mathlib/Topology/ContinuousMap/Basic.lean b/Mathlib/Topology/ContinuousMap/Basic.lean index 03f388dad1e034..20d2c2d917f380 100644 --- a/Mathlib/Topology/ContinuousMap/Basic.lean +++ b/Mathlib/Topology/ContinuousMap/Basic.lean @@ -212,8 +212,6 @@ each term. This is a version of `Equiv.piCurry` for continuous maps. def sigmaEquiv : (∀ i, C(X i, A)) ≃ C((Σ i, X i), A) where toFun := sigma invFun f i := f.comp (sigmaMk i) - left_inv := by intro; ext; simp - right_inv := by intro; ext; simp end Sigma @@ -244,8 +242,6 @@ each term def piEquiv : (∀ i, C(A, X i)) ≃ C(A, ∀ i, X i) where toFun := pi invFun f i := (eval i).comp f - left_inv := by intro; ext; simp [pi] - right_inv := by intro; ext; simp [pi] /-- Combine a collection of bundled continuous maps `C(X i, Y i)` into a bundled continuous map `C(∀ i, X i, ∀ i, Y i)`. -/ diff --git a/Mathlib/Topology/ContinuousMap/Bounded/Basic.lean b/Mathlib/Topology/ContinuousMap/Bounded/Basic.lean index d7b18a2f765823..4d6d79e86aef66 100644 --- a/Mathlib/Topology/ContinuousMap/Bounded/Basic.lean +++ b/Mathlib/Topology/ContinuousMap/Bounded/Basic.lean @@ -217,6 +217,12 @@ theorem dist_eq_iSup : dist f g = ⨆ x : α, dist (f x) (g x) := by theorem nndist_eq_iSup : nndist f g = ⨆ x : α, nndist (f x) (g x) := Subtype.ext <| dist_eq_iSup.trans <| by simp_rw [val_eq_coe, coe_iSup, coe_nndist] +theorem edist_eq_iSup : edist f g = ⨆ x, edist (f x) (g x) := by + simp_rw [edist_nndist, nndist_eq_iSup] + refine ENNReal.coe_iSup ⟨nndist f g, ?_⟩ + rintro - ⟨x, hx, rfl⟩ + exact nndist_coe_le_nndist x + theorem tendsto_iff_tendstoUniformly {ι : Type*} {F : ι → α →ᵇ β} {f : α →ᵇ β} {l : Filter ι} : Tendsto F l (𝓝 f) ↔ TendstoUniformly (fun i => F i) f l := Iff.intro diff --git a/Mathlib/Topology/ContinuousMap/Bounded/Normed.lean b/Mathlib/Topology/ContinuousMap/Bounded/Normed.lean index 1ce5689f03c082..5aff0f8684c870 100644 --- a/Mathlib/Topology/ContinuousMap/Bounded/Normed.lean +++ b/Mathlib/Topology/ContinuousMap/Bounded/Normed.lean @@ -225,6 +225,9 @@ theorem nnnorm_const_eq [Nonempty α] (b : β) : ‖const α b‖₊ = ‖b‖ theorem nnnorm_eq_iSup_nnnorm : ‖f‖₊ = ⨆ x : α, ‖f x‖₊ := Subtype.ext <| (norm_eq_iSup_norm f).trans <| by simp_rw [val_eq_coe, NNReal.coe_iSup, coe_nnnorm] +theorem enorm_eq_iSup_enorm : ‖f‖ₑ = ⨆ x, ‖f x‖ₑ := by + simpa only [← edist_zero_eq_enorm] using edist_eq_iSup + theorem abs_diff_coe_le_dist : ‖f x - g x‖ ≤ dist f g := by rw [dist_eq_norm] exact (f - g).norm_coe_le_norm x diff --git a/Mathlib/Topology/ContinuousMap/Compact.lean b/Mathlib/Topology/ContinuousMap/Compact.lean index 1ce25a6a93e40b..e99894dfdbab4d 100644 --- a/Mathlib/Topology/ContinuousMap/Compact.lean +++ b/Mathlib/Topology/ContinuousMap/Compact.lean @@ -127,6 +127,18 @@ theorem dist_lt_iff (C0 : (0 : ℝ) < C) : dist f g < C ↔ ∀ x : α, dist (f rw [← dist_mkOfCompact, dist_lt_iff_of_compact C0] simp only [mkOfCompact_apply] +theorem dist_eq_iSup : dist f g = ⨆ x, dist (f x) (g x) := by + simp [← isometryEquivBoundedOfCompact α β |>.dist_eq f g, + BoundedContinuousFunction.dist_eq_iSup] + +theorem nndist_eq_iSup : nndist f g = ⨆ x, nndist (f x) (g x) := by + simp [← isometryEquivBoundedOfCompact α β |>.nndist_eq f g, + BoundedContinuousFunction.nndist_eq_iSup] + +theorem edist_eq_iSup : edist f g = ⨆ (x : α), edist (f x) (g x) := by + simp [← isometryEquivBoundedOfCompact α β |>.edist_eq f g, + BoundedContinuousFunction.edist_eq_iSup] + instance {R} [Zero R] [Zero β] [PseudoMetricSpace R] [SMul R β] [IsBoundedSMul R β] : IsBoundedSMul R C(α, β) where dist_smul_pair' r f g := by @@ -210,6 +222,9 @@ theorem nnnorm_eq_iSup_nnnorm : ‖f‖₊ = ⨆ x : α, ‖f x‖₊ := theorem norm_eq_iSup_norm : ‖f‖ = ⨆ x : α, ‖f x‖ := (mkOfCompact f).norm_eq_iSup_norm +theorem enorm_eq_iSup_enorm : ‖f‖ₑ = ⨆ x, ‖f x‖ₑ := + (mkOfCompact f).enorm_eq_iSup_enorm + -- A version with better keys instance {X : Type*} [TopologicalSpace X] (K : TopologicalSpace.Compacts X) : CompactSpace (K : Set X) := diff --git a/Mathlib/Topology/ContinuousMap/CompactlySupported.lean b/Mathlib/Topology/ContinuousMap/CompactlySupported.lean index b1821a0632188a..291ec6b0074bc4 100644 --- a/Mathlib/Topology/ContinuousMap/CompactlySupported.lean +++ b/Mathlib/Topology/ContinuousMap/CompactlySupported.lean @@ -120,8 +120,6 @@ def ContinuousMap.liftCompactlySupported [CompactSpace α] : C(α, β) ≃ C_c( { toFun := f hasCompactSupport' := HasCompactSupport.of_compactSpace f } invFun f := f - left_inv _ := rfl - right_inv _ := rfl variable {γ : Type*} [TopologicalSpace γ] [Zero γ] @@ -647,38 +645,107 @@ continuous `ℝ≥0`-valued function. -/ noncomputable def nnrealPart (f : C_c(α, ℝ)) : C_c(α, ℝ≥0) where toFun := Real.toNNReal.comp f.toFun continuous_toFun := Continuous.comp continuous_real_toNNReal f.continuous - hasCompactSupport' := by - apply HasCompactSupport.comp_left f.hasCompactSupport' Real.toNNReal_zero + hasCompactSupport' := HasCompactSupport.comp_left f.hasCompactSupport' Real.toNNReal_zero @[simp] lemma nnrealPart_apply (f : C_c(α, ℝ)) (x : α) : f.nnrealPart x = Real.toNNReal (f x) := rfl +lemma nnrealPart_neg_eq_zero_of_nonneg {f : C_c(α, ℝ)} (hf : 0 ≤ f) : (-f).nnrealPart = 0 := by + ext x + simpa using hf x + +lemma nnrealPart_smul_pos (f : C_c(α, ℝ)) {a : ℝ} (ha : 0 ≤ a) : + (a • f).nnrealPart = a.toNNReal • f.nnrealPart := by + ext x + simp only [nnrealPart_apply, coe_smul, Pi.smul_apply, Real.coe_toNNReal', smul_eq_mul, + NNReal.coe_mul, ha, sup_of_le_left] + rcases le_total 0 (f x) with hfx | hfx + · simp [ha, hfx, mul_nonneg] + · simp [mul_nonpos_iff, ha, hfx] + +lemma nnrealPart_smul_neg (f : C_c(α, ℝ)) {a : ℝ} (ha : a ≤ 0) : + (a • f).nnrealPart = (-a).toNNReal • (-f).nnrealPart := by + ext x + simp only [nnrealPart_apply, coe_smul, Pi.smul_apply, smul_eq_mul, Real.coe_toNNReal', coe_neg, + Pi.neg_apply, NNReal.coe_mul] + rcases le_total 0 (f x) with hfx | hfx + · simp [mul_nonpos_iff, ha, hfx] + · simp [ha, hfx, mul_nonneg_of_nonpos_of_nonpos] + +lemma nnrealPart_add_le_add_nnrealPart (f g : C_c(α, ℝ)) : + (f + g).nnrealPart ≤ f.nnrealPart + g.nnrealPart := by + intro x + simpa using Real.toNNReal_add_le + +lemma exists_add_nnrealPart_add_eq (f g : C_c(α, ℝ)) : ∃ (h : C_c(α, ℝ≥0)), + (f + g).nnrealPart + h = f.nnrealPart + g.nnrealPart ∧ + (-f + -g).nnrealPart + h = (-f).nnrealPart + (-g).nnrealPart := by + obtain ⟨h, hh⟩ := CompactlySupportedContinuousMap.exists_add_of_le + (nnrealPart_add_le_add_nnrealPart f g) + use h + refine ⟨hh, ?_⟩ + ext x + simp only [coe_add, Pi.add_apply, nnrealPart_apply, coe_neg, Pi.neg_apply, NNReal.coe_add, + Real.coe_toNNReal', ← neg_add] + have hhx : (f x + g x) ⊔ 0 + ↑(h x) = f x ⊔ 0 + g x ⊔ 0 := by + rw [← Real.coe_toNNReal', ← Real.coe_toNNReal', ← Real.coe_toNNReal', ← NNReal.coe_add, + ← NNReal.coe_add] + have hhx' : ((f + g).nnrealPart + h) x = (f.nnrealPart + g.nnrealPart) x := by congr + simp only [coe_add, Pi.add_apply, nnrealPart_apply, Real.coe_toNNReal'] at hhx' + exact congrArg toReal hhx' + rcases le_total 0 (f x) with hfx | hfx + · rcases le_total 0 (g x) with hgx | hgx + · simp only [hfx, hgx, add_nonneg, sup_of_le_left, add_eq_left, coe_eq_zero] at hhx + simp [hhx, hfx, hgx, add_nonpos] + · rcases le_total 0 (f x + g x) with hfgx | hfgx + · simp only [hfgx, sup_of_le_left, add_assoc, hfx, hgx, sup_of_le_right, add_zero, + add_eq_left] at hhx + rw [sup_of_le_right (neg_nonpos.mpr hfx), sup_of_le_left (neg_nonneg.mpr hgx), + sup_of_le_right (neg_nonpos.mpr hfgx)] + linarith + · simp only [hfgx, sup_of_le_right, zero_add, hfx, sup_of_le_left, hgx, add_zero] at hhx + rw [sup_of_le_right (neg_nonpos.mpr hfx), sup_of_le_left (neg_nonneg.mpr hgx), + sup_of_le_left (neg_nonneg.mpr hfgx), hhx] + ring + · rcases le_total 0 (g x) with hgx | hgx + · rcases le_total 0 (f x + g x) with hfgx | hfgx + · simp only [hfgx, sup_of_le_left, add_comm, hfx, sup_of_le_right, hgx, zero_add] at hhx + rw [sup_of_le_left (neg_nonneg.mpr hfx), sup_of_le_right (neg_nonpos.mpr hgx), + sup_of_le_right (neg_nonpos.mpr hfgx), zero_add, add_zero] + linarith + · simp only [hfgx, sup_of_le_right, zero_add, hfx, hgx, sup_of_le_left] at hhx + rw [sup_of_le_left (neg_nonneg.mpr hfx), sup_of_le_right (neg_nonpos.mpr hgx), + sup_of_le_left (neg_nonneg.mpr hfgx), hhx] + ring + · simp only [(add_nonpos hfx hgx), sup_of_le_right, zero_add, hfx, hgx, add_zero, + coe_eq_zero] at hhx + rw [sup_of_le_left (neg_nonneg.mpr hfx), + sup_of_le_left (neg_nonneg.mpr hgx), + sup_of_le_left (neg_nonneg.mpr (add_nonpos hfx hgx)), hhx, neg_add_rev, NNReal.coe_zero, + add_zero] + ring + /-- The compactly supported continuous `ℝ≥0`-valued function as a compactly supported `ℝ`-valued function. -/ noncomputable def toReal (f : C_c(α, ℝ≥0)) : C_c(α, ℝ) := f.compLeft ContinuousMap.coeNNRealReal -@[simp] -lemma toReal_apply (f : C_c(α, ℝ≥0)) (x : α) : f.toReal x = f x := compLeft_apply rfl _ _ - +@[simp] lemma toReal_apply (f : C_c(α, ℝ≥0)) (x : α) : f.toReal x = f x := compLeft_apply rfl _ _ @[simp] lemma toReal_nonneg {f : C_c(α, ℝ≥0)} : 0 ≤ f.toReal := fun _ ↦ by simp - @[simp] lemma toReal_add (f g : C_c(α, ℝ≥0)) : (f + g).toReal = f.toReal + g.toReal := by ext; simp @[simp] lemma toReal_smul (r : ℝ≥0) (f : C_c(α, ℝ≥0)) : (r • f).toReal = r • f.toReal := by ext; simp [NNReal.smul_def] +@[simp] lemma nnrealPart_sub_nnrealPart_neg (f : C_c(α, ℝ)) : - (nnrealPart f).toReal - (nnrealPart (-f)).toReal = f := by - ext x - simp + (nnrealPart f).toReal - (nnrealPart (-f)).toReal = f := by ext x; simp -/-- The compactly supported continuous `ℝ≥0`-valued function as a compactly supported `ℝ`-valued -function. -/ +/-- The map `toReal` defined as a `ℝ≥0`-linear map. -/ noncomputable def toRealLinearMap : C_c(α, ℝ≥0) →ₗ[ℝ≥0] C_c(α, ℝ) where toFun := toReal map_add' f g := by ext x; simp - map_smul' a f := by ext x; simp [NNReal.smul_def] + map_smul' a f := by ext x; simp @[simp, norm_cast] lemma coe_toRealLinearMap : (toRealLinearMap : C_c(α, ℝ≥0) → C_c(α, ℝ)) = toReal := rfl @@ -688,6 +755,14 @@ lemma toRealLinearMap_apply (f : C_c(α, ℝ≥0)) : toRealLinearMap f = f.toRea lemma toRealLinearMap_apply_apply (f : C_c(α, ℝ≥0)) (x : α) : toRealLinearMap f x = (f x).toReal := by simp +@[simp] +lemma nnrealPart_toReal_eq (f : C_c(α, ℝ≥0)) : nnrealPart (toReal f) = f := by ext x; simp + +@[simp] +lemma nnrealPart_neg_toReal_eq (f : C_c(α, ℝ≥0)) : nnrealPart (- toReal f) = 0 := by ext x; simp + +section toNNRealLinear + /-- For a positive linear functional `Λ : C_c(α, ℝ) → ℝ`, define a `ℝ≥0`-linear map. -/ noncomputable def toNNRealLinear (Λ : C_c(α, ℝ) →ₗ[ℝ] ℝ) (hΛ : ∀ f, 0 ≤ f → 0 ≤ Λ f) : C_c(α, ℝ≥0) →ₗ[ℝ≥0] ℝ≥0 where @@ -699,13 +774,59 @@ noncomputable def toNNRealLinear (Λ : C_c(α, ℝ) →ₗ[ℝ] ℝ) (hΛ : ∀ lemma toNNRealLinear_apply (Λ : C_c(α, ℝ) →ₗ[ℝ] ℝ) (hΛ) (f : C_c(α, ℝ≥0)) : toNNRealLinear Λ hΛ f = Λ (toReal f) := rfl -@[simp] lemma toNNRealLinear_inj (Λ₁ Λ₂ : C_c(α, ℝ) →ₗ[ℝ] ℝ) (hΛ₁ hΛ₂) : +@[simp] +lemma toNNRealLinear_inj (Λ₁ Λ₂ : C_c(α, ℝ) →ₗ[ℝ] ℝ) (hΛ₁ hΛ₂) : toNNRealLinear Λ₁ hΛ₁ = toNNRealLinear Λ₂ hΛ₂ ↔ Λ₁ = Λ₂ := by simp only [LinearMap.ext_iff, NNReal.eq_iff, toNNRealLinear_apply] refine ⟨fun h f ↦ ?_, fun h f ↦ by rw [LinearMap.ext h]⟩ rw [← nnrealPart_sub_nnrealPart_neg f] simp_rw [map_sub, h] +end toNNRealLinear + +section toRealLinear + +/-- For a positive linear functional `Λ : C_c(α, ℝ≥0) → ℝ≥0`, define a `ℝ`-linear map. -/ +noncomputable def toRealLinear (Λ : C_c(α, ℝ≥0) →ₗ[ℝ≥0] ℝ≥0) : C_c(α, ℝ) →ₗ[ℝ] ℝ where + toFun := fun f => Λ (nnrealPart f) - Λ (nnrealPart (- f)) + map_add' f g := by + simp only [neg_add_rev] + obtain ⟨h, hh⟩ := exists_add_nnrealPart_add_eq f g + rw [← add_zero ((Λ (f + g).nnrealPart).toReal - (Λ (-g + -f).nnrealPart).toReal), + ← sub_self (Λ h).toReal, sub_add_sub_comm, ← NNReal.coe_add, ← NNReal.coe_add, + ← LinearMap.map_add, ← LinearMap.map_add, hh.1, add_comm (-g) (-f), hh.2] + simp only [map_add, NNReal.coe_add] + ring + map_smul' a f := by + rcases le_total 0 a with ha | ha + · rw [RingHom.id_apply, smul_eq_mul, ← (smul_neg a f), nnrealPart_smul_pos f ha, + nnrealPart_smul_pos (-f) ha] + simp [sup_of_le_left ha, mul_sub] + · simp only [RingHom.id_apply, smul_eq_mul, ← (smul_neg a f), + nnrealPart_smul_neg f ha, nnrealPart_smul_neg (-f) ha, map_smul, + NNReal.coe_mul, Real.coe_toNNReal', neg_neg, sup_of_le_left (neg_nonneg.mpr ha)] + ring + +lemma toRealLinear_apply {Λ : C_c(α, ℝ≥0) →ₗ[ℝ≥0] ℝ≥0} (f : C_c(α, ℝ)) : + toRealLinear Λ f = Λ (nnrealPart f) - Λ (nnrealPart (-f)) := rfl + +lemma toRealLinear_nonneg (Λ : C_c(α, ℝ≥0) →ₗ[ℝ≥0] ℝ≥0) (g : C_c(α, ℝ)) (hg : 0 ≤ g) : + 0 ≤ toRealLinear Λ g := by + simp [toRealLinear_apply, nnrealPart_neg_eq_zero_of_nonneg hg] + +@[simp] +lemma eq_toRealLinear_toReal (Λ : C_c(α, ℝ≥0) →ₗ[ℝ≥0] ℝ≥0) (f : C_c(α, ℝ≥0)) : + toRealLinear Λ (toReal f) = Λ f:= by + simp [toRealLinear_apply] + +@[simp] +lemma eq_toNNRealLinear_toRealLinear (Λ : C_c(α, ℝ≥0) →ₗ[ℝ≥0] ℝ≥0) : + toNNRealLinear (toRealLinear Λ) (toRealLinear_nonneg Λ) = Λ := by + ext f + simp + +end toRealLinear + end CompactlySupportedContinuousMap end NonnegativePart diff --git a/Mathlib/Topology/ContinuousMap/ContinuousMapZero.lean b/Mathlib/Topology/ContinuousMap/ContinuousMapZero.lean index b28637f17b2b88..f06927a049f395 100644 --- a/Mathlib/Topology/ContinuousMap/ContinuousMapZero.lean +++ b/Mathlib/Topology/ContinuousMap/ContinuousMapZero.lean @@ -367,6 +367,10 @@ variable {α : Type*} {𝕜 : Type*} {R : Type*} [TopologicalSpace α] [CompactS noncomputable instance [MetricSpace R] [Zero R]: MetricSpace C(α, R)₀ := ContinuousMapZero.isUniformEmbedding_toContinuousMap.comapMetricSpace _ +lemma isometry_toContinuousMap [MetricSpace R] [Zero R] : + Isometry (toContinuousMap : C(α, R)₀ → C(α, R)) := + fun _ _ ↦ rfl + noncomputable instance [NormedAddCommGroup R] : Norm C(α, R)₀ where norm f := ‖(f : C(α, R))‖ diff --git a/Mathlib/Topology/ContinuousMap/Units.lean b/Mathlib/Topology/ContinuousMap/Units.lean index 33ee7dd738c7bf..f4e7726af42d0f 100644 --- a/Mathlib/Topology/ContinuousMap/Units.lean +++ b/Mathlib/Topology/ContinuousMap/Units.lean @@ -43,8 +43,6 @@ def unitsLift : C(X, Mˣ) ≃ C(X, M)ˣ where continuous_toFun := continuous_induced_rng.2 <| (f : C(X, M)).continuous.prodMk <| MulOpposite.continuous_op.comp (↑f⁻¹ : C(X, M)).continuous } - left_inv f := by ext; rfl - right_inv f := by ext; rfl @[to_additive (attr := simp)] lemma unitsLift_apply_inv_apply (f : C(X, Mˣ)) (x : X) : diff --git a/Mathlib/Topology/ContinuousMap/ZeroAtInfty.lean b/Mathlib/Topology/ContinuousMap/ZeroAtInfty.lean index 4cf65c11e95f0d..75f5c8cd3aca6f 100644 --- a/Mathlib/Topology/ContinuousMap/ZeroAtInfty.lean +++ b/Mathlib/Topology/ContinuousMap/ZeroAtInfty.lean @@ -133,12 +133,6 @@ def ContinuousMap.liftZeroAtInfty [CompactSpace α] : C(α, β) ≃ C₀(α, β) continuous_toFun := f.continuous zero_at_infty' := by simp } invFun f := f - left_inv f := by - ext - rfl - right_inv f := by - ext - rfl /-- A continuous function on a compact space is automatically a continuous function vanishing at infinity. This is not an instance to avoid type class loops. -/ diff --git a/Mathlib/Topology/Defs/Basic.lean b/Mathlib/Topology/Defs/Basic.lean index fa8096638db852..01155dfcc2d109 100644 --- a/Mathlib/Topology/Defs/Basic.lean +++ b/Mathlib/Topology/Defs/Basic.lean @@ -7,6 +7,9 @@ import Mathlib.Order.SetNotation import Mathlib.Tactic.Continuity import Mathlib.Tactic.FunProp import Mathlib.Tactic.MkIffOfInductiveProp +import Mathlib.Tactic.ToAdditive +import Mathlib.Util.AssertExists + /-! # Basic definitions about topological spaces diff --git a/Mathlib/Topology/FiberBundle/Basic.lean b/Mathlib/Topology/FiberBundle/Basic.lean index e68dcbffe0d90a..ec9dd6ebb32707 100644 --- a/Mathlib/Topology/FiberBundle/Basic.lean +++ b/Mathlib/Topology/FiberBundle/Basic.lean @@ -680,7 +680,7 @@ instance fiberBundle : FiberBundle F Z.Fiber where rw [(Z.localTrivAt b).nhds_eq_comap_inf_principal (mk_mem_localTrivAt_source _ _ _), comap_inf, comap_principal, comap_comap] simp only [Function.comp_def, localTrivAt_apply_mk, Trivialization.coe_coe, - ← (isEmbedding_prodMk b).nhds_eq_comap] + ← (isEmbedding_prodMkRight b).nhds_eq_comap] convert_to 𝓝 x = 𝓝 x ⊓ 𝓟 univ · congr exact eq_univ_of_forall (mk_mem_localTrivAt_source Z _) diff --git a/Mathlib/Topology/Homotopy/HomotopyGroup.lean b/Mathlib/Topology/Homotopy/HomotopyGroup.lean index 7f8e6500b83692..632c6abcd6359c 100644 --- a/Mathlib/Topology/Homotopy/HomotopyGroup.lean +++ b/Mathlib/Topology/Homotopy/HomotopyGroup.lean @@ -384,7 +384,6 @@ def genLoopHomeoOfIsEmpty (N x) [IsEmpty N] : Ω^ N X x ≃ₜ X where toFun f := f 0 invFun y := ⟨ContinuousMap.const _ y, fun _ ⟨i, _⟩ => isEmptyElim i⟩ left_inv f := by ext; exact congr_arg f (Subsingleton.elim _ _) - right_inv _ := rfl continuous_invFun := ContinuousMap.const'.2.subtype_mk _ /-- The homotopy "group" indexed by an empty type is in bijection with @@ -421,7 +420,6 @@ def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where rintro y ⟨i, iH | iH⟩ <;> cases Unique.eq_default i <;> apply (congr_arg p iH).trans exacts [p.source, p.target]⟩ left_inv p := by ext y; exact congr_arg p (eq_const_of_unique y).symm - right_inv p := by ext; rfl /- TODO (?): deducing this from `homotopyGroupEquivFundamentalGroup` would require combination of `CategoryTheory.Functor.mapAut` and diff --git a/Mathlib/Topology/MetricSpace/DilationEquiv.lean b/Mathlib/Topology/MetricSpace/DilationEquiv.lean index 1890e9185c0343..3bb1ed0663dbcf 100644 --- a/Mathlib/Topology/MetricSpace/DilationEquiv.lean +++ b/Mathlib/Topology/MetricSpace/DilationEquiv.lean @@ -191,7 +191,16 @@ lemma _root_.IsometryEquiv.toDilationEquiv_apply (e : X ≃ᵢ Y) (x : X) : @[simp] lemma _root_.IsometryEquiv.toDilationEquiv_symm (e : X ≃ᵢ Y) : - e.toDilationEquiv.symm = e.symm.toDilationEquiv := + e.symm.toDilationEquiv = e.toDilationEquiv.symm := + rfl + +@[simp] +lemma _root_.IsometryEquiv.coe_toDilationEquiv (e : X ≃ᵢ Y) : ⇑e.toDilationEquiv = e := + rfl + +@[simp] +lemma _root_.IsometryEquiv.coe_symm_toDilationEquiv (e : X ≃ᵢ Y) : + ⇑e.toDilationEquiv.symm = e.symm := rfl @[simp] @@ -209,12 +218,16 @@ def toHomeomorph (e : X ≃ᵈ Y) : X ≃ₜ Y where continuous_invFun := Dilation.toContinuous e.symm __ := e.toEquiv +@[simp] +lemma toHomeomorph_symm (e : X ≃ᵈ Y) : e.symm.toHomeomorph = e.toHomeomorph.symm := + rfl + @[simp] lemma coe_toHomeomorph (e : X ≃ᵈ Y) : ⇑e.toHomeomorph = e := rfl @[simp] -lemma toHomeomorph_symm (e : X ≃ᵈ Y) : e.toHomeomorph.symm = e.symm.toHomeomorph := +lemma coe_symm_toHomeomorph (e : X ≃ᵈ Y) : ⇑e.toHomeomorph.symm = e.symm := rfl end PseudoEMetricSpace diff --git a/Mathlib/Topology/OmegaCompletePartialOrder.lean b/Mathlib/Topology/OmegaCompletePartialOrder.lean index 63c74713798caf..7649b349e472d2 100644 --- a/Mathlib/Topology/OmegaCompletePartialOrder.lean +++ b/Mathlib/Topology/OmegaCompletePartialOrder.lean @@ -129,10 +129,6 @@ theorem scottContinuous_of_continuous {α β} [OmegaCompletePartialOrder α] tauto theorem continuous_of_scottContinuous {α β} [OmegaCompletePartialOrder α] - [OmegaCompletePartialOrder β] (f : Scott α → Scott β) - (hf : ωScottContinuous f) : Continuous f := by - rw [continuous_def] - intro s hs - dsimp only [IsOpen, TopologicalSpace.IsOpen, Scott.IsOpen] - simp_rw [mem_preimage, mem_def, ← Function.comp_def] - apply ωScottContinuous.comp hs hf + [OmegaCompletePartialOrder β] (f : Scott α → Scott β) (hf : ωScottContinuous f) : + Continuous f := by + rw [continuous_def]; exact fun s hs ↦ hs.comp hf diff --git a/Mathlib/Topology/Path.lean b/Mathlib/Topology/Path.lean index aec8ccb7153a11..6eca68c3436aa7 100644 --- a/Mathlib/Topology/Path.lean +++ b/Mathlib/Topology/Path.lean @@ -495,20 +495,20 @@ theorem trans_pi_eq_pi_trans (γ₀ : ∀ i, Path (as i) (bs i)) (γ₁ : ∀ i, end Pi -/-! #### Pointwise multiplication/addition of two paths in a topological (additive) group -/ +/-! #### Pointwise operations on paths in a topological (additive) group -/ -/-- Pointwise multiplication of paths in a topological group. The additive version is probably more -useful. -/ -@[to_additive "Pointwise addition of paths in a topological additive group."] +/-- Pointwise multiplication of paths in a topological group. -/ +@[to_additive (attr := simps!) "Pointwise addition of paths in a topological additive group."] protected def mul [Mul X] [ContinuousMul X] {a₁ b₁ a₂ b₂ : X} (γ₁ : Path a₁ b₁) (γ₂ : Path a₂ b₂) : Path (a₁ * a₂) (b₁ * b₂) := (γ₁.prod γ₂).map continuous_mul -@[to_additive (attr := simp)] -protected theorem mul_apply [Mul X] [ContinuousMul X] {a₁ b₁ a₂ b₂ : X} (γ₁ : Path a₁ b₁) - (γ₂ : Path a₂ b₂) (t : unitInterval) : (γ₁.mul γ₂) t = γ₁ t * γ₂ t := - rfl +/-- Pointwise inversion of paths in a topological group. -/ +@[to_additive (attr := simps!) "Pointwise negation of paths in a topological group."] +def inv {a b : X} [Inv X] [ContinuousInv X] (γ : Path a b) : + Path a⁻¹ b⁻¹ := + γ.map continuous_inv /-! #### Truncating a path -/ diff --git a/Mathlib/Topology/Sets/Closeds.lean b/Mathlib/Topology/Sets/Closeds.lean index 6673e92549d793..d85844cb4d7f76 100644 --- a/Mathlib/Topology/Sets/Closeds.lean +++ b/Mathlib/Topology/Sets/Closeds.lean @@ -405,8 +405,6 @@ The equivalence between `IrreducibleCloseds α` and `{x : Set α // IsIrreducibl def equivSubtype : IrreducibleCloseds α ≃ { x : Set α // IsIrreducible x ∧ IsClosed x } where toFun a := ⟨a.1, a.2, a.3⟩ invFun a := ⟨a.1, a.2.1, a.2.2⟩ - left_inv := fun ⟨_, _, _⟩ => rfl - right_inv := fun ⟨_, _, _⟩ => rfl /-- The equivalence between `IrreducibleCloseds α` and `{x : Set α // IsClosed x ∧ IsIrreducible x }`. @@ -415,8 +413,6 @@ The equivalence between `IrreducibleCloseds α` and `{x : Set α // IsClosed x def equivSubtype' : IrreducibleCloseds α ≃ { x : Set α // IsClosed x ∧ IsIrreducible x } where toFun a := ⟨a.1, a.3, a.2⟩ invFun a := ⟨a.1, a.2.2, a.2.1⟩ - left_inv := fun ⟨_, _, _⟩ => rfl - right_inv := fun ⟨_, _, _⟩ => rfl variable (α) in /-- The equivalence `IrreducibleCloseds α ≃ { x : Set α // IsIrreducible x ∧ IsClosed x }` is an diff --git a/Mathlib/Topology/Sets/CompactOpenCovered.lean b/Mathlib/Topology/Sets/CompactOpenCovered.lean new file mode 100644 index 00000000000000..3ee2c05bdb344c --- /dev/null +++ b/Mathlib/Topology/Sets/CompactOpenCovered.lean @@ -0,0 +1,136 @@ +/- +Copyright (c) 2025 Christian Merten. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Merten +-/ +import Mathlib.Topology.Spectral.Prespectral + +/-! +# Compact open covered sets + +In this file we define the notion of a compact-open covered set with respect to a family of +maps `fᵢ : X i → S`. A set `U` is compact-open covered by the family `fᵢ` if it is the finite +union of images of compact open sets in the `X i`. + +This notion is not interesting, if the `fᵢ` are open maps (see `IsCompactOpenCovered.of_isOpenMap`). + +This is used to define the fpqc topology of schemes, there a cover is given by a family of flat +morphisms such that every compact open is compact-open covered. + +## Main results + +- `IsCompactOpenCovered.of_isOpenMap`: If all the `fᵢ` are open maps, then every compact open + of `S` is compact-open covered. +-/ + +open TopologicalSpace Opens + +/-- A set `U` is compact-open covered by the family `fᵢ : X i → S`, if +`U` is the finite union of images of compact open sets in the `X i`. -/ +def IsCompactOpenCovered {S ι : Type*} {X : ι → Type*} (f : ∀ i, X i → S) + [∀ i, TopologicalSpace (X i)] (U : Set S) : Prop := + ∃ (s : Set ι) (_ : s.Finite) (V : ∀ i ∈ s, Opens (X i)), + (∀ (i : ι) (h : i ∈ s), IsCompact (V i h).1) ∧ + ⋃ (i : ι) (h : i ∈ s), (f i) '' (V i h) = U + +namespace IsCompactOpenCovered + +variable {S ι : Type*} {X : ι → Type*} {f : ∀ i, X i → S} [∀ i, TopologicalSpace (X i)] {U : Set S} + +lemma empty : IsCompactOpenCovered f ∅ := + ⟨∅, Set.finite_empty, fun _ _ ↦ ⟨∅, isOpen_empty⟩, fun _ _ ↦ isCompact_empty, by simp⟩ + +lemma iff_of_unique [Unique ι] : + IsCompactOpenCovered f U ↔ ∃ (V : Opens (X default)), IsCompact V.1 ∧ f default '' V.1 = U := by + refine ⟨fun ⟨s, hs, V, hc, hcov⟩ ↦ ?_, fun ⟨V, hc, h⟩ ↦ ?_⟩ + · by_cases h : s = ∅ + · aesop + · obtain rfl : s = {default} := by + rw [← Set.univ_unique, Subsingleton.eq_univ_of_nonempty (Set.nonempty_iff_ne_empty.mpr h)] + aesop + · refine ⟨{default}, Set.finite_singleton _, fun i h ↦ h ▸ V, fun i ↦ ?_, by simpa⟩ + rintro rfl + simpa + +lemma id_iff_isOpen_and_isCompact [TopologicalSpace S] : + IsCompactOpenCovered (fun _ : Unit ↦ id) U ↔ IsOpen U ∧ IsCompact U := by + rw [iff_of_unique] + refine ⟨fun ⟨V, hV, heq⟩ ↦ ?_, fun ⟨ho, hc⟩ ↦ ⟨⟨U, ho⟩, hc, by simp⟩⟩ + simp only [id_eq, Set.image_id', carrier_eq_coe, ← heq] at heq ⊢ + exact ⟨V.2, hV⟩ + +lemma iff_isCompactOpenCovered_sigmaMk : + IsCompactOpenCovered f U ↔ + IsCompactOpenCovered (fun (_ : Unit) (p : Σ i : ι, X i) ↦ f p.1 p.2) U := by + classical + rw [iff_of_unique (ι := Unit)] + refine ⟨fun ⟨s, hs, V, hc, hU⟩ ↦ ?_, fun ⟨V, hc, heq⟩ ↦ ?_⟩ + · refine ⟨⟨s.sigma fun i ↦ if h : i ∈ s then V i h else ∅, isOpen_sigma_iff.mpr ?_⟩, ?_, ?_⟩ + · intro i + by_cases h : i ∈ s + · simpa [h] using (V _ _).2 + · simp [h] + · dsimp only + exact Set.isCompact_sigma hs fun i ↦ (by aesop) + · aesop + · obtain ⟨s, t, hs, hc, heq'⟩ := hc.sigma_exists_finite_sigma_eq + have (i : ι) (hi : i ∈ s) : IsOpen (t i) := by + rw [← Set.mk_preimage_sigma (t := t) hi] + exact isOpen_sigma_iff.mp (heq' ▸ V.2) i + refine ⟨s, hs, fun i hi ↦ ⟨t i, this i hi⟩, fun i _ ↦ hc i, ?_⟩ + simp_rw [coe_mk, ← heq, ← heq', Set.image_sigma_eq_iUnion, Function.comp_apply] + +lemma of_iUnion_eq_of_finite (s : Set (Set S)) (hs : ⋃ t ∈ s, t = U) (hf : s.Finite) + (H : ∀ t ∈ s, IsCompactOpenCovered f t) : IsCompactOpenCovered f U := by + rw [iff_isCompactOpenCovered_sigmaMk, iff_of_unique] + have (t) (h : t ∈ s) : ∃ (V : Opens (Σ i, X i)), + IsCompact V.1 ∧ (fun p ↦ f p.fst p.snd) '' V.carrier = t := by + have := H t h + rwa [iff_isCompactOpenCovered_sigmaMk, iff_of_unique] at this + choose V hVeq hVc using this + refine ⟨⨆ (t : s), V t t.2, ?_, ?_⟩ + · simp only [Opens.iSup_mk, Opens.carrier_eq_coe, Opens.coe_iSup, Opens.coe_mk] + have : Finite s := hf + exact isCompact_iUnion (fun _ ↦ hVeq _ _) + · simp [Set.image_iUnion, ← hs] + aesop + +/-- If `U` is compact-open covered and the `X i` have a basis of compact opens, +`U` can be written as the union of images of elements of the basis. -/ +lemma exists_mem_of_isBasis {B : ∀ i, Set (Opens (X i))} (hB : ∀ i, IsBasis (B i)) + (hBc : ∀ (i : ι), ∀ U ∈ B i, IsCompact U.1) + {U : Set S} (hU : IsCompactOpenCovered f U) : + ∃ (n : ℕ) (a : Fin n → ι) (V : ∀ i, Opens (X (a i))), + (∀ i, V i ∈ B (a i)) ∧ ⋃ i, f (a i) '' V i = U := by + suffices h : ∃ (κ : Type _) (_ : Finite κ) (a : κ → ι) (V : ∀ i, Opens (X (a i))), + (∀ i, V i ∈ B (a i)) ∧ (∀ i, IsCompact (V i).1) ∧ ⋃ i, f (a i) '' V i = U by + obtain ⟨κ, _, a, V, hB, hc, hU⟩ := h + cases nonempty_fintype κ + refine ⟨Fintype.card κ, a ∘ (Fintype.equivFin κ).symm, fun i ↦ V _, fun i ↦ hB _, ?_⟩ + simp [← hU, ← (Fintype.equivFin κ).symm.surjective.iUnion_comp, Function.comp_apply] + obtain ⟨s, hs, V, hc, hunion⟩ := hU + choose Us UsB hUsf hUs using fun i : s ↦ (hB i.1).exists_finite_of_isCompact (hc i i.2) + let σ := Σ i : s, Us i + have : Finite s := hs + have (i) : Finite (Us i) := hUsf i + refine ⟨σ, inferInstance, fun i ↦ i.1.1, fun i ↦ i.2.1, fun i ↦ UsB _ (by simp), + fun _ ↦ hBc _ _ (UsB _ (by simp)), ?_⟩ + rw [← hunion] + ext x + simp_rw [Set.mem_iUnion] + refine ⟨fun ⟨i, hi, o, ho⟩ ↦ by aesop, fun ⟨i, hi, h, hmem, heq⟩ ↦ ?_⟩ + rw [hUs ⟨i, hi⟩, coe_sSup, Set.mem_iUnion] at hmem + obtain ⟨a, ha⟩ := hmem + simp only [Set.mem_iUnion, SetLike.mem_coe, exists_prop] at ha + use ⟨⟨i, hi⟩, ⟨a, ha.1⟩⟩, h, ha.2, heq + +lemma of_isOpenMap [TopologicalSpace S] [∀ i, PrespectralSpace (X i)] + (hfc : ∀ i, Continuous (f i)) (h : ∀ i, IsOpenMap (f i)) + {U : Set S} (hs : ∀ x ∈ U, ∃ i y, f i y = x) (hU : IsOpen U) (hc : IsCompact U) : + IsCompactOpenCovered f U := by + rw [iff_isCompactOpenCovered_sigmaMk, iff_of_unique] + refine (isOpenMap_sigma.mpr h).exists_opens_image_eq_of_prespectralSpace + (continuous_sigma_iff.mpr hfc) (fun x hx ↦ ?_) hU hc + simpa using hs x hx + +end IsCompactOpenCovered diff --git a/Mathlib/Topology/Sets/Opens.lean b/Mathlib/Topology/Sets/Opens.lean index f198fa1363e7f0..0808cb8a2f4f73 100644 --- a/Mathlib/Topology/Sets/Opens.lean +++ b/Mathlib/Topology/Sets/Opens.lean @@ -313,12 +313,27 @@ theorem IsBasis.isCompact_open_iff_eq_finite_iUnion {ι : Type*} (b : ι → Ope simp · exact hb' +lemma IsBasis.exists_finite_of_isCompact {B : Set (Opens α)} (hB : IsBasis B) {U : Opens α} + (hU : IsCompact U.1) : ∃ Us ⊆ B, Us.Finite ∧ U = sSup Us := by + classical + obtain ⟨Us', hsub, hsup⟩ := isBasis_iff_cover.mp hB U + obtain ⟨t, ht⟩ := hU.elim_finite_subcover (fun s : Us' ↦ s.1) (fun s ↦ s.1.2) (by simp [hsup]) + refine ⟨Finset.image Subtype.val t, subset_trans (by simp) hsub, Finset.finite_toSet _, ?_⟩ + exact le_antisymm (subset_trans ht (by simp)) (le_trans (sSup_le_sSup (by simp)) hsup.ge) + lemma IsBasis.le_iff {α} {t₁ t₂ : TopologicalSpace α} {Us : Set (Opens α)} (hUs : @IsBasis α t₂ Us) : t₁ ≤ t₂ ↔ ∀ U ∈ Us, IsOpen[t₁] U := by conv_lhs => rw [hUs.eq_generateFrom] simp [Set.subset_def, le_generateFrom_iff_subset_isOpen] +lemma isBasis_sigma {ι : Type*} {α : ι → Type*} [∀ i, TopologicalSpace (α i)] + {B : ∀ i, Set (Opens (α i))} (hB : ∀ i, IsBasis (B i)) : + IsBasis (⋃ i : ι, (fun U ↦ ⟨Sigma.mk i '' U.1, isOpenMap_sigmaMk _ U.2⟩) '' B i) := by + convert TopologicalSpace.IsTopologicalBasis.sigma hB + simp only [IsBasis, Set.image_iUnion, ← Set.image_comp] + aesop + lemma IsBasis.of_isInducing {B : Set (Opens β)} (H : IsBasis B) {f : α → β} (h : IsInducing f) : IsBasis { ⟨f ⁻¹' U, U.2.preimage h.continuous⟩ | U ∈ B } := by simp only [IsBasis] at H ⊢ diff --git a/Mathlib/Topology/Spectral/Prespectral.lean b/Mathlib/Topology/Spectral/Prespectral.lean index 04303b7a4c29be..6e759db0b271d6 100644 --- a/Mathlib/Topology/Spectral/Prespectral.lean +++ b/Mathlib/Topology/Spectral/Prespectral.lean @@ -73,6 +73,21 @@ lemma PrespectralSpace.of_isClosedEmbedding [PrespectralSpace Y] (f : X → Y) (hf : IsClosedEmbedding f) : PrespectralSpace X := .of_isInducing f hf.isInducing hf.isProperMap.isSpectralMap +instance PrespectralSpace.sigma {ι : Type*} (X : ι → Type*) [∀ i, TopologicalSpace (X i)] + [∀ i, PrespectralSpace (X i)] : PrespectralSpace (Σ i, X i) := + .of_isTopologicalBasis (IsTopologicalBasis.sigma fun i ↦ isTopologicalBasis) fun U hU ↦ by + simp_rw [Set.mem_iUnion] at hU + obtain ⟨i, V, hV, rfl⟩ := hU + exact hV.2.image continuous_sigmaMk + +variable (X) in +lemma PrespectralSpace.isBasis_opens [PrespectralSpace X] : + TopologicalSpace.Opens.IsBasis { U : Opens X | IsCompact (U : Set X) } := by + dsimp only [TopologicalSpace.Opens.IsBasis] + convert isTopologicalBasis (X := X) + ext s + exact ⟨fun ⟨V, hV, heq⟩ ↦ heq ▸ ⟨V.2, hV⟩, fun h ↦ ⟨⟨s, h.1⟩, h.2, rfl⟩⟩ + /-- In a prespectral space, the lattice of opens is determined by its lattice of compact opens. -/ def PrespectralSpace.opensEquiv [PrespectralSpace X] : Opens X ≃o Order.Ideal (CompactOpens X) where @@ -102,3 +117,28 @@ def PrespectralSpace.opensEquiv [PrespectralSpace X] : intro H x hxU obtain ⟨W, ⟨h₁, h₂⟩, hxW, hWU⟩ := isTopologicalBasis.exists_subset_of_mem_open hxU U.2 exact H ⟨⟨W, h₂⟩, h₁⟩ hWU hxW + +open TopologicalSpace Opens in +/-- If `X` has a basis of compact opens and `f : X → S` is open, every +compact open of `S` is the image of a compact open of `X`. -/ +lemma IsOpenMap.exists_opens_image_eq_of_prespectralSpace [PrespectralSpace X] {f : X → Y} + (hfc : Continuous f) (h : IsOpenMap f) {U : Set Y} (hs : U ⊆ Set.range f) (hU : IsOpen U) + (hc : IsCompact U) : ∃ (V : Opens X), IsCompact V.1 ∧ f '' V = U := by + obtain ⟨Us, hUs, heq⟩ := TopologicalSpace.Opens.isBasis_iff_cover.mp + (PrespectralSpace.isBasis_opens X) ⟨f ⁻¹' U, hU.preimage hfc⟩ + obtain ⟨t, ht⟩ := by + refine hc.elim_finite_subcover (fun s : Us ↦ f '' s.1) (fun s ↦ h _ s.1.2) (fun x hx ↦ ?_) + obtain ⟨x, rfl⟩ := hs hx + obtain ⟨i, hi, hx⟩ := mem_sSup.mp <| by rwa [← heq] + exact Set.mem_iUnion.mpr ⟨⟨i, hi⟩, x, hx, rfl⟩ + refine ⟨⨆ s ∈ t, s.1, ?_, ?_⟩ + · simp only [iSup_mk, carrier_eq_coe, coe_mk] + exact t.finite_toSet.isCompact_biUnion fun i _ ↦ hUs i.2 + · simp only [iSup_mk, carrier_eq_coe, Set.iUnion_coe_set, coe_mk, Set.image_iUnion] + convert_to ⋃ i ∈ t, f '' i.1 = U + · aesop + · refine subset_antisymm (fun x ↦ ?_) ht + simp_rw [Set.mem_iUnion] + rintro ⟨i, hi, x, hx, rfl⟩ + have := heq ▸ mem_sSup.mpr ⟨i.1, i.2, hx⟩ + exact this diff --git a/Mathlib/Topology/UniformSpace/Defs.lean b/Mathlib/Topology/UniformSpace/Defs.lean index 6896ce14cc551b..894ba4d796906c 100644 --- a/Mathlib/Topology/UniformSpace/Defs.lean +++ b/Mathlib/Topology/UniformSpace/Defs.lean @@ -178,6 +178,14 @@ theorem Monotone.compRel [Preorder β] {f g : β → Set (α × α)} (hf : Monot theorem compRel_mono {f g h k : Set (α × α)} (h₁ : f ⊆ h) (h₂ : g ⊆ k) : f ○ g ⊆ h ○ k := fun _ ⟨z, h, h'⟩ => ⟨z, h₁ h, h₂ h'⟩ +@[gcongr] +theorem compRel_left_mono {f g h : Set (α × α)} (h₁ : f ⊆ g) : f ○ h ⊆ g ○ h := + fun _ ⟨z, h, h'⟩ => ⟨z, h₁ h, h'⟩ + +@[gcongr] +theorem compRel_right_mono {f g h : Set (α × α)} (h₁ : g ⊆ h) : f ○ g ⊆ f ○ h := + fun _ ⟨z, h, h'⟩ => ⟨z, h, h₁ h'⟩ + theorem prodMk_mem_compRel {a b c : α} {s t : Set (α × α)} (h₁ : (a, c) ∈ s) (h₂ : (c, b) ∈ t) : (a, b) ∈ s ○ t := ⟨c, h₁, h₂⟩ @@ -474,10 +482,10 @@ theorem comp_symm_of_uniformity {s : Set (α × α)} (hs : s ∈ 𝓤 α) : let ⟨t', ht', ht'₁, ht'₂⟩ := symm_of_uniformity ht₁ ⟨t', ht', ht'₁ _ _, Subset.trans (monotone_id.compRel monotone_id ht'₂) ht₂⟩ -theorem uniformity_le_symm : 𝓤 α ≤ @Prod.swap α α <$> 𝓤 α := by +theorem uniformity_le_symm : 𝓤 α ≤ map Prod.swap (𝓤 α) := by rw [map_swap_eq_comap_swap]; exact tendsto_swap_uniformity.le_comap -theorem uniformity_eq_symm : 𝓤 α = @Prod.swap α α <$> 𝓤 α := +theorem uniformity_eq_symm : 𝓤 α = map Prod.swap (𝓤 α) := le_antisymm uniformity_le_symm symm_le_uniformity @[simp] @@ -541,13 +549,9 @@ theorem comp_comp_symm_mem_uniformity_sets {s : Set (α × α)} (hs : s ∈ 𝓤 rcases comp_symm_mem_uniformity_sets w_in with ⟨t, t_in, t_symm, t_sub⟩ use t, t_in, t_symm have : t ⊆ t ○ t := subset_comp_self_of_mem_uniformity t_in - -- Porting note: Needed the following `have`s to make `mono` work - have ht := Subset.refl t - have hw := Subset.refl w calc - t ○ t ○ t ⊆ w ○ t := by mono - _ ⊆ w ○ (t ○ t) := by mono - _ ⊆ w ○ w := by mono + t ○ t ○ t ⊆ w ○ (t ○ t) := by gcongr + _ ⊆ w ○ w := by gcongr _ ⊆ s := w_sub /-! @@ -630,7 +634,7 @@ theorem mem_nhds_uniformity_iff_right {x : α} {s : Set α} : theorem mem_nhds_uniformity_iff_left {x : α} {s : Set α} : s ∈ 𝓝 x ↔ { p : α × α | p.2 = x → p.1 ∈ s } ∈ 𝓤 α := by rw [uniformity_eq_symm, mem_nhds_uniformity_iff_right] - simp only [map_def, mem_map, preimage_setOf_eq, Prod.snd_swap, Prod.fst_swap] + simp only [mem_map, preimage_setOf_eq, Prod.snd_swap, Prod.fst_swap] theorem nhdsWithin_eq_comap_uniformity_of_mem {x : α} {T : Set α} (hx : x ∈ T) (S : Set α) : 𝓝[S] x = (𝓤 α ⊓ 𝓟 (T ×ˢ S)).comap (Prod.mk x) := by diff --git a/Mathlib/Topology/UniformSpace/UniformConvergenceTopology.lean b/Mathlib/Topology/UniformSpace/UniformConvergenceTopology.lean index 46bd2f9353dc13..1bbaee1b3c4970 100644 --- a/Mathlib/Topology/UniformSpace/UniformConvergenceTopology.lean +++ b/Mathlib/Topology/UniformSpace/UniformConvergenceTopology.lean @@ -784,8 +784,6 @@ Here we formulate it as a `UniformEquiv`. -/ def uniformEquivUniformFun (h : univ ∈ 𝔖) : (α →ᵤ[𝔖] β) ≃ᵤ (α →ᵤ β) where toFun f := UniformFun.ofFun <| toFun _ f invFun f := ofFun _ <| UniformFun.toFun f - left_inv _ := rfl - right_inv _ := rfl uniformContinuous_toFun := by simp only [UniformContinuous, (UniformFun.hasBasis_uniformity _ _).tendsto_right_iff] intro U hU diff --git a/Mathlib/Topology/VectorBundle/Basic.lean b/Mathlib/Topology/VectorBundle/Basic.lean index 2cf85e0bf880b8..ae2a46077eb080 100644 --- a/Mathlib/Topology/VectorBundle/Basic.lean +++ b/Mathlib/Topology/VectorBundle/Basic.lean @@ -279,7 +279,7 @@ theorem coe_coordChangeL' (e e' : Trivialization F (π F E)) [e.IsLinear R] [e'. theorem symm_coordChangeL (e e' : Trivialization F (π F E)) [e.IsLinear R] [e'.IsLinear R] {b : B} (hb : b ∈ e'.baseSet ∩ e.baseSet) : (e.coordChangeL R e' b).symm = e'.coordChangeL R e b := by apply ContinuousLinearEquiv.toLinearEquiv_injective - rw [coe_coordChangeL' e' e hb, (coordChangeL R e e' b).symm_toLinearEquiv, + rw [coe_coordChangeL' e' e hb, (coordChangeL R e e' b).toLinearEquiv_symm, coe_coordChangeL' e e' hb.symm, LinearEquiv.trans_symm, LinearEquiv.symm_symm] theorem coordChangeL_apply (e e' : Trivialization F (π F E)) [e.IsLinear R] [e'.IsLinear R] {b : B} diff --git a/MathlibTest/Clear!.lean b/MathlibTest/ClearExclamation.lean similarity index 100% rename from MathlibTest/Clear!.lean rename to MathlibTest/ClearExclamation.lean diff --git a/MathlibTest/GCongr/implications.lean b/MathlibTest/GCongr/implications.lean new file mode 100644 index 00000000000000..24d3933050dd22 --- /dev/null +++ b/MathlibTest/GCongr/implications.lean @@ -0,0 +1,16 @@ +import Mathlib.Tactic.GCongr + +variable {a b c d : Prop} + +example (h : a → b) : (a ∧ ¬b) ∨ c → (b ∧ ¬a) ∨ c := by gcongr +example (h : a → b) : (a ∧ ¬b) ∨ c → (b ∧ ¬a) ∨ c := by gcongr ?_ ∧ ¬?_ ∨ c + +example (h : d → b) : (a ∨ b ∧ c → d) → (a ∨ d ∧ c → b) := by gcongr +example (h : d → b) : (a ∨ b ∧ c → d) → (a ∨ d ∧ c → b) := by gcongr a ∨ ?_ ∧ c → ?_ + +example (h : a → b) : ¬ ¬ ¬ b → ¬ ¬ ¬ a := by gcongr +example (h : a → b) : ¬ ¬ ¬ b → ¬ ¬ ¬ a := by gcongr ¬ ¬ ¬ ?_ + +example (h : a → b) : (∃ i, ∀ j, i ∧ b → j) → (∃ i, ∀ j, i ∧ a → j) := by gcongr +example (h : a → b) : (∃ i, ∀ j, i ∧ b → j) → (∃ i, ∀ j, i ∧ a → j) := by + gcongr ∃ i, ∀ j, i ∧ ?_ → j diff --git a/MathlibTest/GCongr/inequalities.lean b/MathlibTest/GCongr/inequalities.lean index 8bab9c9f6cae92..5f92761ab6ba7a 100644 --- a/MathlibTest/GCongr/inequalities.lean +++ b/MathlibTest/GCongr/inequalities.lean @@ -248,4 +248,18 @@ example {ι : Type*} [Fintype ι] {f g : ι → ℝ} : ∏ i, f i ^ 2 ≤ ∏ i, · guard_target = f i ≤ g i exact test_sorry +/-! Test that `gcongr` can deal with `_ ≤ _ → _ ≤ _` and `_ < _ → _ < _` -/ + +example {a b : ℕ} (h1 : a ≤ 0) (h2 : 0 ≤ b) : b ≤ a + 1 → 0 ≤ 0 + 1 := by gcongr +example {a b : ℕ} (h1 : a ≤ 0) (_h2 : 0 ≤ b) : b ≤ a + 1 → b ≤ 0 + 1 := by gcongr +example {a b : ℕ} (_h1 : a ≤ 0) (h2 : 0 ≤ b) : b ≤ a + 1 → 0 ≤ a + 1 := by gcongr + +example {a b : ℕ} (h1 : a ≤ 0) (h2 : 0 ≤ b) : b < a + 1 → 0 < 0 + 1 := by gcongr +example {a b : ℕ} (h1 : a ≤ 0) (_h2 : 0 ≤ b) : b < a + 1 → b < 0 + 1 := by gcongr +example {a b : ℕ} (_h1 : a ≤ 0) (h2 : 0 ≤ b) : b < a + 1 → 0 < a + 1 := by gcongr + +/-! Test that `gcongr` with a pattern doesn't complain about type class inference problems. -/ + +example {a b : ℕ} (h1 : a ≤ 0) (h2 : 0 ≤ b) : b ≤ a + 1 → 0 ≤ 0 + 1 := by gcongr ?_ ≤ ?_ + _ + end GCongrTests diff --git a/MathlibTest/GCongr/mod.lean b/MathlibTest/GCongr/mod.lean index a093450955c4b4..11ae818cb151df 100644 --- a/MathlibTest/GCongr/mod.lean +++ b/MathlibTest/GCongr/mod.lean @@ -6,7 +6,8 @@ Authors: Heather Macbeth import Mathlib.Data.Int.ModEq /-! # Modular arithmetic tests for the `gcongr` tactic -/ -set_option autoImplicit true + +variable {a b n x : ℤ} example (ha : a ≡ 2 [ZMOD 4]) : a * b ^ 2 + a ^ 2 * b + 3 ≡ 2 * b ^ 2 + 2 ^ 2 * b + 3 [ZMOD 4] := by gcongr @@ -16,16 +17,29 @@ example (ha : a ≡ 4 [ZMOD 5]) (hb : b ≡ 3 [ZMOD 5]) : gcongr example (hb : 3 ≡ b [ZMOD 5]) : b ^ 2 ≡ 3 ^ 2 [ZMOD 5] := by gcongr +example (hb : 3 ≡ b [ZMOD 5]) : b ^ 2 ≡ 3 ^ 2 [ZMOD 5] := by gcongr ?_ ^ 2 example (hb : 3 ≡ b [ZMOD 5]) : b ^ 2 ≡ 3 ^ 2 [ZMOD 5] := by rel [hb] example (hx : x ≡ 0 [ZMOD 3]) : x ^ 3 ≡ 0 ^ 3 [ZMOD 3] := by gcongr +example (hx : x ≡ 0 [ZMOD 3]) : x ^ 3 ≡ 0 ^ 3 [ZMOD 3] := by gcongr ?_ ^ 3 example (hx : x ≡ 2 [ZMOD 3]) : x ^ 3 ≡ 2 ^ 3 [ZMOD 3] := by gcongr example (hn : n ≡ 1 [ZMOD 3]) : n ^ 3 + 7 * n ≡ 1 ^ 3 + 7 * 1 [ZMOD 3] := by gcongr +example (hn : n ≡ 1 [ZMOD 3]) : n ^ 3 + 7 * n ≡ 1 ^ 3 + 7 * 1 [ZMOD 3] := by gcongr ?_ ^ 3 + 7 * ?_ example (hn : n ≡ 1 [ZMOD 3]) : n ^ 3 + 7 * n ≡ 1 ^ 3 + 7 * 1 [ZMOD 3] := by rel [hn] example (hn : n ≡ 1 [ZMOD 2]) : 5 * n ^ 2 + 3 * n + 7 ≡ 5 * 1 ^ 2 + 3 * 1 + 7 [ZMOD 2] := by gcongr example (hx : x ≡ 3 [ZMOD 5]) : x ^ 5 ≡ 3 ^ 5 [ZMOD 5] := by gcongr + +/-! Test that `gcongr` can deal with `_ ≡ _ [ZMOD _] → _ ≡ _ [ZMOD _]` -/ +variable {a b : ℤ} + +example (h1 : 0 ≡ a [ZMOD 7]) (h2 : 0 ≡ b [ZMOD 7]) : b ≡ a + 1 [ZMOD 7] → 0 ≡ 0 + 1 [ZMOD 7] := by + gcongr +example (h1 : 0 ≡ a [ZMOD 7]) (_h2 : 0 ≡ b [ZMOD 7]) : b ≡ a + 1 [ZMOD 7] → b ≡ 0 + 1 [ZMOD 7] := by + gcongr +example (_h1 : 0 ≡ a [ZMOD 7]) (h2 : 0 ≡ b [ZMOD 7]) : b ≡ a + 1 [ZMOD 7] → 0 ≡ a + 1 [ZMOD 7] := by + gcongr diff --git a/MathlibTest/GRewrite.lean b/MathlibTest/GRewrite.lean new file mode 100644 index 00000000000000..c872d81da59647 --- /dev/null +++ b/MathlibTest/GRewrite.lean @@ -0,0 +1,296 @@ +/- +Copyright (c) 2023 Sebastian Zimmer. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sebastian Zimmer, Mario Carneiro, Heather Macbeth +-/ +import Mathlib.Data.Int.ModEq +import Mathlib.Tactic.GRewrite +import Mathlib.Tactic.GCongr +import Mathlib.Tactic.NormNum +import Mathlib.Algebra.Order.Ring.Abs +import Mathlib.Algebra.Order.BigOperators.Ring.Finset + +/- In many examples in this module, we rewrite expressions which do not make it into the final term. + +This only happens because we are writing tests where we resolve the goal with a universal axiom, it +would not happen in real proofs, so we disable the resulting linter warnings. +-/ +set_option linter.unusedVariables false + +private axiom test_sorry : ∀ {α}, α + +variable {α : Type*} [CommRing α] [LinearOrder α] [IsStrictOrderedRing α] (a b c d e : α) + +section inequalities + +example (h₁ : a ≤ b) (h₂ : b ≤ c) : a + 5 ≤ c + 6 := by + grw [h₁, h₂] + guard_target =ₛ c + 5 ≤ c + 6 + grw [show (5 : α) < 6 by norm_num] + +example (h₁ : a ≤ b) (h₂ : b ≤ c) : c + 6 > a + 5 := by + grw [h₁, h₂] + guard_target =ₛ c + 6 > c + 5 + exact test_sorry + +example (h₁ : c ≤ b) : a + 5 ≤ b + 6 := by + grw [← h₁] + guard_target =ₛ a + 5 ≤ c + 6 + exact test_sorry + +example (h₁ : c ≤ b) (h₂ : a + 5 < c + 6) : a + 5 < b + 6 := by + grw [← h₁] + guard_target =ₛ a + 5 < c + 6 + exact h₂ + +example (h₁ : a + e ≤ b + e) (h₂ : b < c) (h₃ : c ≤ d) : a + e ≤ d + e := by + grw [h₂, h₃] at h₁ + guard_hyp h₁ :ₛ a + e ≤ d + e + exact h₁ + +example (f g : α → α) (h : ∀ x : α, f x ≤ g x) (h₂ : g a + g b ≤ 5) : f a + f b ≤ 5 := by + grw [h] + guard_target =ₛ g a + f b ≤ 5 + grw [h] + guard_target =ₛ g a + g b ≤ 5 + grw [h₂] + +example (f g : α → α) (h : ∀ x : α, f x < g x) : f a ≤ g b := by + grw [h, ← h b] + guard_target =ₛ g a ≤ f b + exact test_sorry + +example (h₁ : a ≥ b) (h₂ : 0 ≤ c) : a * c ≥ 100 - a := by + grw [h₁] + guard_target =ₛ b * c ≥ 100 - b + exact test_sorry + +example {n : ℕ} (bound : n ≤ 5) : n ≤ 10 := by + have h' : 5 ≤ 10 := by decide + grw [h'] at bound + assumption + +example (h₁ : a ≤ b) : a + 5 ≤ b + 6 := by grw [h₁, show (5 : α) ≤ 6 by norm_num] + +example (h₁ : a ≤ b) : a * 5 ≤ b * 5 := by grw [h₁] + +example (h₁ : a ≤ b) (h₂ : c ≥ 0) : a * c ≤ b * c := by grw [h₁] + +example (h₁ : a ≤ b) : a * c ≤ b * c := by + grw [h₁] + guard_target =ₛ 0 ≤ c + exact test_sorry + +/- This example has behaviour which might be weaker than some users would desire: it would be +mathematically sound to transform the goal here to `2 * y ≤ z`, not `2 * y < z`. + +However, the current behavior is easier to implement, and preserves the form of the goal (`?_ < z`), +which is a useful invariant. -/ +example {x y z : ℤ} (hx : x < y) : 2 * x < z := by + grw [hx] + guard_target =ₛ 2 * y < z + exact test_sorry + +end inequalities + +section subsets + +variable (X Y Z W : Set α) + +example (h₁ : X ⊆ Y) (h₂ : Y ⊆ Z) (h₃ : a ∈ X) : False := by + grw [h₁] at h₃ + guard_hyp h₃ :ₛ a ∈ Y + grw [h₂] at h₃ + guard_hyp h₃ :ₛ a ∈ Z + exact test_sorry + +example (h₁ : Y ⊇ W) : X ⊂ (Y ∪ Z) := by + grw [h₁] + guard_target =ₛ X ⊂ (W ∪ Z) + exact test_sorry + +example (h₁ : W ⊂ Y) (h₂ : X ⊂ (W ∪ Z)) : X ⊂ (Y ∪ Z) := by + grw [← h₁] + guard_target =ₛ X ⊂ (W ∪ Z) + exact h₂ + +example {a b : Nat} (h : a < b) (f : Nat → Nat) (hf : ∀ i, 0 ≤ f i) : + ∑ i ∈ ({x | x ≤ a} : Set Nat), f i ≤ ∑ i ∈ ({x | x ≤ b} : Set Nat), f i := by + grw [h] + +end subsets + +section rationals + +example (x x' y z w : ℚ) (h0 : x' = x) (h₁ : x < z) (h₂ : w ≤ y + 4) (h₃ : z + 1 < 5 * w) : + x' + 1 < 5 * (y + 4) := by + grw [h0, h₁, ← h₂] + exact h₃ + +example {x y z : ℚ} (f g : ℚ → ℚ) (h : ∀ t, f t = g t) : 2 * f x * f y * f x ≤ z := by + grw [h] + guard_target =ₛ 2 * g x * f y * g x ≤ z + exact test_sorry + +example {x y a b : ℚ} (h : x ≤ y) (h1 : a ≤ 3 * x) : 2 * x ≤ b := by + grw [h] at h1 + guard_hyp h1 :ₛ a ≤ 3 * y + exact test_sorry + +end rationals + +section modeq + +example {n : ℤ} (hn : n ≡ 1 [ZMOD 3]) : n ^ 3 + 7 * n ≡ 2 [ZMOD 3] := by grw [hn]; decide + +example {a b : ℤ} (h1 : a ≡ 3 [ZMOD 5]) (h2 : b ≡ a ^ 2 + 1 [ZMOD 5]) : + a ^ 2 + b ^ 2 ≡ 4 [ZMOD 5] := by + grw [h1] at h2 ⊢ + guard_hyp h2 :ₛ b ≡ 3 ^ 2 + 1 [ZMOD 5] + guard_target =ₛ 3 ^ 2 + b ^ 2 ≡ 4 [ZMOD 5] + grw [h2] + decide + +end modeq + +section wildcard + +/-! Rewriting at a wildcard `*`, i.e. `grw [h] at *`, will sometimes include a rewrite at `h` itself +and sometimes not, according to whether the generalized rewrite is valid or not; this is the case +approximately (not exactly) when the relation in the type of `h` is an equivalence relation. See +examples below of it occurring for `≡ [ZMOD 5]` and not occurring for `<`. + +Having `grw [h] at *` rewrite at `h` itself is a bit weird, but is consistent with the behaviour of +`rw` (for the equivalence relation `=`). +-/ + +example {a b : ℤ} (h1 : a ≡ 3 [ZMOD 5]) (h2 : b ≡ a ^ 2 + 1 [ZMOD 5]) : + a ^ 2 + b ^ 2 ≡ 4 [ZMOD 5] := by + grw [h1] at * + guard_hyp h1 :ₛ 3 ≡ 3 [ZMOD 5] -- `grw [h1] at *` rewrites at `h1` + guard_hyp h2 :ₛ b ≡ 3 ^ 2 + 1 [ZMOD 5] + guard_target =ₛ 3 ^ 2 + b ^ 2 ≡ 4 [ZMOD 5] + grw [h2] + decide + +example {x y a b : ℚ} (h : x < y) (h1 : a ≤ 3 * x) : 2 * x ≤ b := by + grw [h] at * + guard_hyp h :ₛ x < y -- `grw [h] at *` does not rewrite at `h` + guard_hyp h1 :ₛ a ≤ 3 * y + guard_target =ₛ 2 * y ≤ b + exact test_sorry + +end wildcard + +section nontransitive_grw_lemmas + +example {s s' t : Set ℕ} (h : s' ⊆ s) (h2 : BddAbove (s ∩ t)) : BddAbove (s' ∩ t) := by + grw [h]; exact h2 + +example {s s' : Set ℕ} (h : s' ⊆ s) (h2 : BddAbove s) : BddAbove s' := by + grw [h]; exact h2 + +example {s s' t : Set α} (h : s ⊆ s') : (s' ∩ t).Nonempty := by + grw [← h] + guard_target =ₛ (s ∩ t).Nonempty + exact test_sorry + +end nontransitive_grw_lemmas + +section + +/-! In the examples in this section, the proposed rewrite is not valid because the constructed +relation does not have its main goals proved by `gcongr` (in the two examples here this is because +the inequality goes in the wrong direction). -/ + +/-- +error: tactic 'grewrite' failed, could not discharge x ≤ y using x ≥ y +case a.h +α : Type u_1 +inst✝² : CommRing α +inst✝¹ : LinearOrder α +inst✝ : IsStrictOrderedRing α +a b✝ c d e : α +x y b : ℚ +h : x ≥ y +⊢ x ≤ y +-/ +#guard_msgs in +example {x y b : ℚ} (h : x ≥ y) : 2 * x ≤ b := by + grw [h] + +example {s s' t : Set α} (h : s' ⊆ s) : (s' ∩ t).Nonempty := by + fail_if_success grw [h] + exact test_sorry + +end + +example {x y a b : ℤ} (h1 : |x| ≤ a) (h2 : |y| ≤ b) : + |x ^ 2 + 2 * x * y| ≤ a ^ 2 + 2 * a * b := by + have : 0 ≤ a := by grw [← h1]; positivity + grw [abs_add, abs_mul, abs_mul, abs_pow, h1, h2, abs_of_nonneg] + norm_num + +example {a b : ℚ} {P : Prop} (hP : P) (h : P → a < b) : False := by + have : 2 * a ≤ 2 * b := by grw [h]; exact hP + exact test_sorry + +example {a b : ℚ} {P Q : Prop} (hP : P) (hQ : Q) (h : P → Q → a < b) : False := by + have : 2 * a ≤ 2 * b := by grw [h ?_ hQ]; exact hP + exact test_sorry + +example {a a' : ℕ} {X : Set ℕ} (h₁ : a + 1 ∈ X) (h₂ : a = a') : False := by + grw [h₂] at h₁ + guard_hyp h₁ :ₛ a' + 1 ∈ X + exact test_sorry + +example {Prime : ℕ → Prop} {a a' : ℕ} (h₁ : Prime (a + 1)) (h₂ : a = a') : False := by + grw [h₂] at h₁ + guard_hyp h₁ :ₛ Prime (a' + 1) + exact test_sorry + +/-- +error: tactic 'grewrite' failed, could not discharge b ≤ a using a ≤ b +case h₁.h +α : Type u_1 +inst✝² : CommRing α +inst✝¹ : LinearOrder α +inst✝ : IsStrictOrderedRing α +a b c d e : α +h₁ : a ≤ b +h₂ : 0 ≤ c +⊢ b ≤ a +-/ +#guard_msgs in +example (h₁ : a ≤ b) (h₂ : 0 ≤ c) : a * c ≥ 100 + a := by + grw [h₁] + +example (h₁ : a ≤ b) (h₂ : 0 ≤ c) : a * c ≥ 100 + a + a := by + nth_grw 2 3 [h₁] + guard_target =ₛ a * c ≥ 100 + b + b + exact test_sorry +section apply + +variable {p q r s : Prop} + +example (n : Nat) (h : p → n=1) (h' : p) : n = 1 := by + apply_rw [← h] + exact h' + +example (n : Nat) (h : p → n=1) (h' : r → s) : p ∧ r → n = 1 ∧ s := by + apply_rw [h, h'] + exact id + +example (n : Nat) (h : p → n=1) (h' : r → s) : p ∧ r → n = 1 ∧ s := by + grw [h] + apply_rw [h'] + gcongr + intro _ + rfl + exact test_sorry + +example (h : p → q) (h' : q → r) : p → r := by + apply_rw [← h] at h' + exact h' + +end apply diff --git a/MathlibTest/RefinedDiscrTree.lean b/MathlibTest/RefinedDiscrTree.lean new file mode 100644 index 00000000000000..93a977731b30d1 --- /dev/null +++ b/MathlibTest/RefinedDiscrTree.lean @@ -0,0 +1,211 @@ +import Mathlib.Lean.Meta.RefinedDiscrTree.Encode +import Mathlib + +open Qq Lean Meta RefinedDiscrTree + +macro "#log_keys" e:term : command => + `(command| run_meta do + for keys in ← encodeExprWithEta (labelledStars := true) q($e) do + logInfo m! "{← keysAsPattern keys}") + +-- eta reduction: +/-- info: Int.succ -/ +#guard_msgs in +#log_keys fun x => Int.succ x + +-- potential eta reduction: +/-- +info: @Function.Bijective ℤ ℤ Int.succ +--- +info: @Function.Bijective ℤ ℤ (λ, Int.succ *) +-/ +#guard_msgs in +run_meta do + let m ← mkFreshExprMVarQ q(ℤ → ℤ) + for keys in ← encodeExprWithEta (labelledStars := true) q(Function.Bijective fun x : Int => Int.succ ($m x)) do + logInfo m! "{← keysAsPattern keys}" + +/-- +info: And (@Function.Bijective ℤ ℤ Int.succ) (@Function.Bijective ℤ ℤ Int.succ) +--- +info: And (@Function.Bijective ℤ ℤ Int.succ) (@Function.Bijective ℤ ℤ (λ, Int.succ *)) +--- +info: And (@Function.Bijective ℤ ℤ (λ, Int.succ *)) (@Function.Bijective ℤ ℤ Int.succ) +--- +info: And (@Function.Bijective ℤ ℤ (λ, Int.succ *)) (@Function.Bijective ℤ ℤ (λ, Int.succ *)) +-/ +#guard_msgs in +run_meta do + let m ← mkFreshExprMVarQ q(ℤ → ℤ) + for keys in ← encodeExprWithEta (labelledStars := true) q((Function.Bijective fun x : Int => Int.succ ($m x)) ∧ + Function.Bijective fun x : Int => Int.succ ($m x)) do + logInfo m! "{← keysAsPattern keys}" + +/-- +info: @Eq *0 (@HAdd.hAdd *0 *0 * * (@HAdd.hAdd *0 *0 * * *1 *1) *2) (@HAdd.hAdd *0 *0 * * *1 a) +-/ +#guard_msgs in +run_meta do + let t ← mkFreshExprMVarQ q(Type) + let _ ← mkFreshExprMVarQ q(Add $t) + let m ← mkFreshExprMVarQ q($t) + let m' ← mkFreshExprMVarQ q($t) + withLocalDeclDQ `a q($t) fun n => do + for keys in ← encodeExprWithEta (labelledStars := true) q($m+$m + $m' = $m + $n) do + logInfo m! "{← keysAsPattern keys}" + +/-- +info: @Function.Bijective *0 *0 (@HAdd.hAdd *0 *0 * * *) +--- +info: @Function.Bijective *0 *0 (λ, @HAdd.hAdd *0 *0 * * * *) +-/ +#guard_msgs in +run_meta do + let t ← mkFreshExprMVarQ q(Type) + let _ ← mkFreshExprMVarQ q(Add $t) + let m ← mkFreshExprMVarQ q($t → $t) + let m' ← mkFreshExprMVarQ q($t → $t) + for keys in ← encodeExprWithEta (labelledStars := true) q(Function.Bijective fun x => $m x + $m' x) do + logInfo m! "{← keysAsPattern keys}" + +/-- info: @OfNat.ofNat ℕ 2 * -/ +#guard_msgs in +#log_keys let x := 2; x + +-- unfolding reducible constants: +/-- info: @LE.le ℕ * 3 2 -/ +#guard_msgs in +#log_keys 2 ≥ 3 + + +/-- info: ℕ → @Eq ℕ #0 5 -/ +#guard_msgs in +#log_keys ∀ x : Nat, x = 5 + +/-- info: ℕ → ℤ -/ +#guard_msgs in +#log_keys Nat → Int + +/-- info: λ, #0 -/ +#guard_msgs in +#log_keys fun x : Nat => x + +open Finset in +/-- info: @Finset.sum ℕ ℕ * (range 10) (λ, #0) -/ +#guard_msgs in +#log_keys ∑ i ∈ range 10, i + +/-- info: @Nat.fold ℕ 10 (λ, λ, λ, @HAdd.hAdd ℕ ℕ * * #0 #2) 1 -/ +#guard_msgs in +#log_keys (10).fold (init := 1) (fun x _ y => y + x) + +/-- info: @HAdd.hAdd (ℕ → ℕ) (ℕ → ℕ) * * (@id ℕ) (λ, #0) 4 -/ +#guard_msgs in +#log_keys ((@id Nat) + fun x : Nat => x) 4 + +/-- info: Nat.sqrt (@HAdd.hAdd (ℕ → ℕ) (ℕ → ℕ) * * (@id ℕ) (λ, #0) 4) -/ +#guard_msgs in +#log_keys Nat.sqrt $ ((@id Nat) + fun x : Nat => x) 4 + +/-- info: Nat.sqrt (@HPow.hPow (ℕ → ℕ) ℕ * * (@id ℕ) 3 6) -/ +#guard_msgs in +#log_keys Nat.sqrt $ (id ^ 3 : Nat → Nat) 6 + +/-- info: Nat.sqrt (@HVAdd.hVAdd ℕ (ℕ → ℕ) * * 4 (@id ℕ) 6) -/ +#guard_msgs in +#log_keys Nat.sqrt $ (4 +ᵥ id : Nat → Nat) 6 + +/-- info: Int.sqrt (@Neg.neg (ℤ → ℤ) * (@id ℤ) 6) -/ +#guard_msgs in +#log_keys Int.sqrt $ (-id : Int → Int) 6 + + + +/-- +info: @Function.Bijective + ℕ + ℕ + (λ, @HAdd.hAdd + ℕ + ℕ + * + * + (@HMul.hMul ℕ ℕ * * #0 3) + (@HDiv.hDiv ℕ ℕ * * 4 (@HPow.hPow ℕ ℕ * * (@HVAdd.hVAdd ℕ ℕ * * 3 (@HSMul.hSMul ℕ ℕ * * 2 5)) #0))) +-/ +#guard_msgs in +#log_keys Function.Bijective fun x => x*3+4/(3+ᵥ2•5)^x + +/-- +info: Nat.sqrt + (@HAdd.hAdd + (ℕ → ℕ) + (ℕ → ℕ) + * + * + (@HVAdd.hVAdd ℕ (ℕ → ℕ) * * (@HSMul.hSMul ℕ ℕ * * 2 1) (@id ℕ)) + (@HDiv.hDiv (ℕ → ℕ) (ℕ → ℕ) * * (@HMul.hMul (ℕ → ℕ) (ℕ → ℕ) * * 4 5) (@HPow.hPow (ℕ → ℕ) ℕ * * (@id ℕ) 9)) + 5) +-/ +#guard_msgs in +#log_keys Nat.sqrt $ ((2•1+ᵥid)+4*5/id^9 : Nat → Nat) 5 + + +/-- info: @Function.Bijective ℕ ℕ (λ, @HPow.hPow ℕ ℕ * * #0 #0) -/ +#guard_msgs in +#log_keys Function.Bijective fun x => x^x + +/-- info: @Function.Bijective ℕ ℕ (λ, @HSMul.hSMul ℕ ℕ * * #0 5) -/ +#guard_msgs in +#log_keys Function.Bijective fun x : Nat => x•5 + +/-- info: @Function.Bijective ℕ ℕ (λ, @HVAdd.hVAdd ℕ ℕ * * #0 #0) -/ +#guard_msgs in +#log_keys Function.Bijective fun x : Nat => x+ᵥx + +/-- info: @id (Sort → (Ring #0) → #1) (λ, λ, @HAdd.hAdd #1 #1 * * 1 2) -/ +#guard_msgs in +#log_keys id fun (α : Type) [Ring α] => (1+2 : α) + +/-- info: @id (Sort → (Ring #0) → #1) (λ, λ, @HSMul.hSMul ℕ #1 * * 2 3) -/ +#guard_msgs in +#log_keys id fun (α : Type) [Ring α] => (2•3 : α) + + +/-- info: @Function.Bijective ℕ ℕ (λ, 4) -/ +#guard_msgs in +#log_keys Function.Bijective fun _ : Nat => 4 + +/-- info: λ, @OfNat.ofNat ℕ 4 * -/ +#guard_msgs in +#log_keys fun _ : Nat => 4 + + +-- metavariables in a lambda body +/-- info: @Function.Bijective ℕ ℕ (λ, *0) -/ +#guard_msgs in +run_meta do + let m ← mkFreshExprMVarQ q(ℕ) + for keys in ← encodeExprWithEta (labelledStars := true) q(Function.Bijective fun _ : Nat => $m) do + logInfo m! "{← keysAsPattern keys}" + +/-- info: λ, *0 -/ +#guard_msgs in +run_meta do + let m ← mkFreshExprMVarQ q(ℕ) + for keys in ← encodeExprWithEta (labelledStars := true) q(fun _ : Nat => $m) do + logInfo m! "{← keysAsPattern keys}" + +/-- info: @Function.Bijective ℕ ℕ (λ, *) -/ +#guard_msgs in +run_meta do + let m ← mkFreshExprMVarQ q(ℕ → ℕ → ℕ) + for keys in ← encodeExprWithEta (labelledStars := true) q(Function.Bijective fun x : Nat => $m x x) do + logInfo m! "{← keysAsPattern keys}" + +/-- info: λ, * -/ +#guard_msgs in +run_meta do + let m ← mkFreshExprMVarQ q(ℕ → ℕ → ℕ) + for keys in ← encodeExprWithEta (labelledStars := true) q(fun x : Nat => $m x x) do + logInfo m! "{← keysAsPattern keys}" diff --git a/MathlibTest/factors.lean b/MathlibTest/factors.lean new file mode 100644 index 00000000000000..766a60f0d37ce4 --- /dev/null +++ b/MathlibTest/factors.lean @@ -0,0 +1,9 @@ +import Mathlib.Tactic.Simproc.Factors + +example : Nat.primeFactorsList 0 = [] := by simp only [Nat.primeFactorsList_ofNat] +example : Nat.primeFactorsList 1 = [] := by simp only [Nat.primeFactorsList_ofNat] +example : Nat.primeFactorsList 2 = [2] := by simp only [Nat.primeFactorsList_ofNat] +example : Nat.primeFactorsList 3 = [3] := by simp only [Nat.primeFactorsList_ofNat] +example : Nat.primeFactorsList 4 = [2, 2] := by simp only [Nat.primeFactorsList_ofNat] +example : Nat.primeFactorsList 12 = [2, 2, 3] := by simp only [Nat.primeFactorsList_ofNat] +example : Nat.primeFactorsList 221 = [13, 17] := by simp only [Nat.primeFactorsList_ofNat] diff --git a/MathlibTest/hint.lean b/MathlibTest/hint.lean index 59e01241e19994..f56c41c2a1f870 100644 --- a/MathlibTest/hint.lean +++ b/MathlibTest/hint.lean @@ -1,6 +1,10 @@ +import Mathlib.Data.ENNReal.Basic +import Mathlib.Data.Nat.Prime.Defs import Mathlib.Tactic.Common +import Mathlib.Tactic.ComputeDegree +import Mathlib.Tactic.FieldSimp +import Mathlib.Tactic.Finiteness import Mathlib.Tactic.Linarith -import Mathlib.Data.Nat.Prime.Defs import Mathlib.Tactic.TautoSet /-- @@ -29,13 +33,14 @@ example {P Q R : Prop} (x : P ∧ Q ∧ R ∧ R) : Q ∧ P ∧ R := by hint /-- info: Try these: • linarith +• field_simp -/ #guard_msgs in example {a b : ℚ} (h : a < b) : ¬ b < a := by hint /-- info: Try these: -• norm_num +• ring -/ #guard_msgs in example : 37^2 - 35^2 = 72 * 2 := by hint @@ -43,6 +48,7 @@ example : 37^2 - 35^2 = 72 * 2 := by hint /-- info: Try these: • decide +• ring_nf • norm_num -/ #guard_msgs in @@ -51,6 +57,8 @@ example : Nat.Prime 37 := by hint /-- info: Try these: • aesop +• ring_nf +• field_simp • norm_num • simp_all only [zero_le, and_true] -/ @@ -94,6 +102,8 @@ example {α} (A B C : Set α) (h1 : A ⊆ B ∪ C) : (A ∩ B) ∪ (A ∩ C) = A /-- info: Try these: • aesop +• ring_nf +• field_simp • norm_num • simp_all only [Nat.not_ofNat_le_one] --- @@ -103,3 +113,33 @@ warning: declaration uses 'sorry' example : 2 ≤ 1 := by hint end tauto_set + +section compute_degree +/-- +info: Try these: +• compute_degree +-/ +#guard_msgs in +open Polynomial in +example : natDegree ((X + 1) : Nat[X]) ≤ 1 := by hint +end compute_degree + +section field_simp +/-- +info: Try these: +• field_simp +• ring_nf +-/ +#guard_msgs in +example (R : Type) (a b : R) [CommRing R] (u₁ : Rˣ) : a /ₚ u₁ + b /ₚ u₁ = (a + b) /ₚ u₁ := by hint +end field_simp + +section finiteness +/-- +info: Try these: +• finiteness +-/ +#guard_msgs in +open ENNReal in +example : (1 : ℝ≥0∞) < ∞ := by hint +end finiteness diff --git a/MathlibTest/norm_num_ext.lean b/MathlibTest/norm_num_ext.lean index 154f728c4d909a..4423b434055fc4 100644 --- a/MathlibTest/norm_num_ext.lean +++ b/MathlibTest/norm_num_ext.lean @@ -17,6 +17,7 @@ import Mathlib.Tactic.NormNum.LegendreSymbol import Mathlib.Tactic.NormNum.Pow import Mathlib.Tactic.NormNum.RealSqrt import Mathlib.Tactic.NormNum.Irrational +import Mathlib.Tactic.Simproc.Factors /-! # Tests for `norm_num` extensions @@ -122,15 +123,6 @@ example : Nat.minFac 121 = 11 := by norm_num1 example : Nat.minFac 221 = 13 := by norm_num1 example : Nat.minFac (2 ^ 19 - 1) = 2 ^ 19 - 1 := by norm_num1 -/- -example : Nat.factors 0 = [] := by norm_num1 -example : Nat.factors 1 = [] := by norm_num1 -example : Nat.factors 2 = [2] := by norm_num1 -example : Nat.factors 3 = [3] := by norm_num1 -example : Nat.factors 4 = [2, 2] := by norm_num1 -example : Nat.factors 12 = [2, 2, 3] := by norm_num1 -example : Nat.factors 221 = [13, 17] := by norm_num1 --/ -- randomized tests example : Nat.gcd 35 29 = 1 := by norm_num1 @@ -195,83 +187,81 @@ example : Nat.Coprime 93 34 := by norm_num1 example : ¬ Nat.Prime 912 := by norm_num1 example : Nat.minFac 912 = 2 := by norm_num1 --- example : Nat.factors 912 = [2, 2, 2, 2, 3, 19] := by norm_num1 example : ¬ Nat.Prime 681 := by norm_num1 example : Nat.minFac 681 = 3 := by norm_num1 --- example : Nat.factors 681 = [3, 227] := by norm_num1 example : ¬ Nat.Prime 728 := by norm_num1 example : Nat.minFac 728 = 2 := by norm_num1 --- example : Nat.factors 728 = [2, 2, 2, 7, 13] := by norm_num1 +example : Nat.primeFactorsList 728 = [2, 2, 2, 7, 13] := by simp example : ¬ Nat.Prime 248 := by norm_num1 example : Nat.minFac 248 = 2 := by norm_num1 --- example : Nat.factors 248 = [2, 2, 2, 31] := by norm_num1 +example : Nat.primeFactorsList 248 = [2, 2, 2, 31] := by simp example : ¬ Nat.Prime 682 := by norm_num1 example : Nat.minFac 682 = 2 := by norm_num1 --- example : Nat.factors 682 = [2, 11, 31] := by norm_num1 +example : Nat.primeFactorsList 682 = [2, 11, 31] := by simp example : ¬ Nat.Prime 115 := by norm_num1 example : Nat.minFac 115 = 5 := by norm_num1 --- example : Nat.factors 115 = [5, 23] := by norm_num1 +example : Nat.primeFactorsList 115 = [5, 23] := by simp example : ¬ Nat.Prime 824 := by norm_num1 example : Nat.minFac 824 = 2 := by norm_num1 --- example : Nat.factors 824 = [2, 2, 2, 103] := by norm_num1 +example : Nat.primeFactorsList 824 = [2, 2, 2, 103] := by simp example : ¬ Nat.Prime 942 := by norm_num1 example : Nat.minFac 942 = 2 := by norm_num1 --- example : Nat.factors 942 = [2, 3, 157] := by norm_num1 +example : Nat.primeFactorsList 942 = [2, 3, 157] := by simp example : ¬ Nat.Prime 34 := by norm_num1 example : Nat.minFac 34 = 2 := by norm_num1 --- example : Nat.factors 34 = [2, 17] := by norm_num1 +example : Nat.primeFactorsList 34 = [2, 17] := by simp example : ¬ Nat.Prime 754 := by norm_num1 example : Nat.minFac 754 = 2 := by norm_num1 --- example : Nat.factors 754 = [2, 13, 29] := by norm_num1 +example : Nat.primeFactorsList 754 = [2, 13, 29] := by simp example : ¬ Nat.Prime 663 := by norm_num1 example : Nat.minFac 663 = 3 := by norm_num1 --- example : Nat.factors 663 = [3, 13, 17] := by norm_num1 +example : Nat.primeFactorsList 663 = [3, 13, 17] := by simp example : ¬ Nat.Prime 923 := by norm_num1 example : Nat.minFac 923 = 13 := by norm_num1 --- example : Nat.factors 923 = [13, 71] := by norm_num1 +example : Nat.primeFactorsList 923 = [13, 71] := by simp example : ¬ Nat.Prime 77 := by norm_num1 example : Nat.minFac 77 = 7 := by norm_num1 --- example : Nat.factors 77 = [7, 11] := by norm_num1 +example : Nat.primeFactorsList 77 = [7, 11] := by simp example : ¬ Nat.Prime 162 := by norm_num1 example : Nat.minFac 162 = 2 := by norm_num1 --- example : Nat.factors 162 = [2, 3, 3, 3, 3] := by norm_num1 +example : Nat.primeFactorsList 162 = [2, 3, 3, 3, 3] := by simp example : ¬ Nat.Prime 669 := by norm_num1 example : Nat.minFac 669 = 3 := by norm_num1 --- example : Nat.factors 669 = [3, 223] := by norm_num1 +example : Nat.primeFactorsList 669 = [3, 223] := by simp example : ¬ Nat.Prime 476 := by norm_num1 example : Nat.minFac 476 = 2 := by norm_num1 --- example : Nat.factors 476 = [2, 2, 7, 17] := by norm_num1 +example : Nat.primeFactorsList 476 = [2, 2, 7, 17] := by simp example : Nat.Prime 251 := by norm_num1 example : Nat.minFac 251 = 251 := by norm_num1 --- example : Nat.factors 251 = [251] := by norm_num1 +example : Nat.primeFactorsList 251 = [251] := by simp example : ¬ Nat.Prime 129 := by norm_num1 example : Nat.minFac 129 = 3 := by norm_num1 --- example : Nat.factors 129 = [3, 43] := by norm_num1 +example : Nat.primeFactorsList 129 = [3, 43] := by simp example : ¬ Nat.Prime 471 := by norm_num1 example : Nat.minFac 471 = 3 := by norm_num1 --- example : Nat.factors 471 = [3, 157] := by norm_num1 +example : Nat.primeFactorsList 471 = [3, 157] := by simp example : ¬ Nat.Prime 851 := by norm_num1 example : Nat.minFac 851 = 23 := by norm_num1 --- example : Nat.factors 851 = [23, 37] := by norm_num1 +example : Nat.primeFactorsList 851 = [23, 37] := by simp /- example : ¬ Squarefree 0 := by norm_num1 diff --git a/MathlibTest/peel.lean b/MathlibTest/peel.lean index 8dad58a210c0b2..131e9bb76231dc 100644 --- a/MathlibTest/peel.lean +++ b/MathlibTest/peel.lean @@ -270,3 +270,34 @@ and #guard_msgs in example (h : ∀ᶠ n : ℕ in atTop, 0 ≤ n) : ∀ᶠ n : ℕ in atBot, 0 ≤ n := by peel 1 h + +/-! Testing **gcongr** on peel goals -/ + +example (p q : ℝ → ℝ → Prop) (h : ∀ ε > 0, ∃ δ > 0, p ε δ) + (hpq : ∀ x y, x > 0 → y > 0 → p x y → q x y) : + ∀ ε > 0, ∃ δ > 0, q ε δ := by + revert h + gcongr with ε hε δ hδ + exact hpq ε δ hε hδ + +example (x y : ℚ) (h : ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, x + n = y + ε) : + ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, x - ε = y - n := by + revert h + gcongr ∀ ε > 0, ∃ N, ∀ n ≥ _, ?_ with ε hε _ n hn + intro this + guard_hyp this : x + ↑n = y + ε + guard_target =ₐ x - ε = y - n + linarith + +example {f : ℝ → ℝ} (h : ∀ x : ℝ, ∀ᶠ y in 𝓝 x, |f y - f x| ≤ |y - x|) : + ∀ x : ℝ, ∀ᶠ y in 𝓝 x, |f y - f x| ^ 2 ≤ |y - x| ^ 2 := by + revert h + gcongr ∀ x, ∀ᶠ y in _, ?_ + intro + gcongr + +example (α : Type*) (f g : Filter α) (p q : α → α → Prop) (h : ∀ᶠ x in f, ∃ᶠ y in g, p x y) + (h₁ : ∀ x y, p x y → q x y) : ∀ᶠ x in f, ∃ᶠ y in g, q x y := by + revert h + gcongr + apply h₁ diff --git a/README.md b/README.md index 8c81d2d744d20f..3146554f322116 100644 --- a/README.md +++ b/README.md @@ -54,10 +54,9 @@ for quick reference. The complete documentation for contributing to ``mathlib`` is located [on the community guide contribute to mathlib](https://leanprover-community.github.io/contribute/index.html) -The process is different from other projects where one should not fork the repository. -Instead write permission for non-master branches should be requested on [Zulip](https://leanprover.zulipchat.com) -by introducing yourself, providing your GitHub handle and what contribution you are planning on doing. -You may want to subscribe to the `mathlib4` stream +You may want to subscribe to the `mathlib4` channel on [Zulip](https://leanprover.zulipchat.com/) to introduce yourself and your plan to the community. +Often you can find community members willing to help you get started and advise you on the fit and +feasibility of your project. * To obtain precompiled `olean` files, run `lake exe cache get`. (Skipping this step means the next step will be very slow.) * To build `mathlib4` run `lake build`. diff --git a/bors.toml b/bors.toml index e549f7e5ad474c..851729c7d29a63 100644 --- a/bors.toml +++ b/bors.toml @@ -1,4 +1,4 @@ -status = ["Build", "Lint style"] +status = ["CI Success"] use_squash_merge = true timeout_sec = 28800 block_labels = ["WIP", "blocked-by-other-PR", "merge-conflict", "awaiting-CI", "bench-after-CI"] diff --git a/docs/Conv/Guide.lean b/docs/Conv/Guide.lean index 582af758995a0b..0f61c31ab1d356 100644 --- a/docs/Conv/Guide.lean +++ b/docs/Conv/Guide.lean @@ -217,7 +217,7 @@ in Lean 4 core. * `guard_expr` and `guard_target` for asserting that certain expressions are equal to others. (Batteries) -* `unreachable!`, which is the same as the `unreachable!` tactic. (Batteriess) +* `unreachable!`, which is the same as the `unreachable!` tactic. (Batteries) * `run_tac doSeq` evaluates a monadic value and runs it as a tactic using `tactic'`. (Mathlib) diff --git a/lake-manifest.json b/lake-manifest.json index 86c56e755bf11a..5e5d675ff64ae3 100644 --- a/lake-manifest.json +++ b/lake-manifest.json @@ -65,7 +65,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "4765a4ee7ab89f407e4bc1d2aa1dbae83bd95e16", + "rev": "5e6a77528fb6cace1f0adf2563e4e1bc1da541ae", "name": "batteries", "manifestFile": "lake-manifest.json", "inputRev": "main", diff --git a/scripts/README.md b/scripts/README.md index 865713874dfa25..bdbf3635094f61 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -14,6 +14,26 @@ to learn about it as well! https://leanprover-community.github.io/install/macos.html If these web pages are deprecated or removed, we should remove these scripts. +**Repository analysis and reporting** +- `user_activity_report.py` + Generates a comprehensive report of all users with repository access and their last commit activity. + Shows username, age of last commit, and access level, sorted by commit recency (most recent first). + + **Features:** + - Fetches repository collaborators and organization members via GitHub API + - Intelligent caching: user lists (24h TTL) and commit data (6h TTL) for performance + - Access level filtering: `--admin` (admin users only), `--write` (write+ access) + - Single user analysis: `--user USERNAME` for debugging specific users + - Result limiting: `--limit N` for testing with smaller datasets + - Inactive user cleanup: `--remove N` generates (but doesn't execute) gh commands + to remove write access from non-admin users inactive for more than N days + - Fallback to contributors API if collaborators access is restricted (`--contributors-only`) + + **Caching:** Results cached in `scripts/users_cache.json` and `scripts/commits_cache.json` + (automatically added to .gitignore). Cache saved after each commit lookup to prevent data loss. + + **Requirements:** `gh` (GitHub CLI) installed and authenticated (`gh auth login`). + **Tools for manual maintenance** - `fix_unused.py` Bulk processing of unused variable warnings, replacing them with `_`. @@ -21,6 +41,20 @@ to learn about it as well! It assumes that the only difference between master and the current status of the PR consists of renames. More precisely, any change on a line that contains a declaration name and is not a rename, will likely confuse the script. +- `migrate_to_fork.py` + Helps contributors migrate from having direct write access to the main repository + to using a fork-based workflow. This comprehensive script automates the entire migration process: + * Validates the current branch (prevents migration of system branches like master, nightly-testing) + * Checks GitHub CLI installation/authentication with OS-specific installation instructions + * Creates or syncs a fork of mathlib4 automatically + * Sets up git remotes correctly (`upstream` for leanprover-community/mathlib4, `origin` for user's fork) + * Detects already-completed migration steps and skips them for efficiency + * Migrates the current branch to the fork with proper upstream tracking + * Intelligently handles existing PRs (migrates main repo PRs to fork-based PRs, detects existing fork PRs) + * Uses fast delete/re-add approach for remote operations to avoid slow branch tracking updates + * Provides comprehensive status reporting and next steps guidance + Run with `python3 scripts/migrate_to_fork.py` (interactive) or `python3 scripts/migrate_to_fork.py -y` (auto-accept). + Requires GitHub CLI (`gh`) installed and authenticated. Safe to run multiple times. **Analyzing Mathlib's import structure** - `unused_in_pole.sh` (followed by an optional ``, defaulting to `Mathlib`) diff --git a/scripts/autolabel.lean b/scripts/autolabel.lean index eb9299f21d7209..c70f4e9f8e26a0 100644 --- a/scripts/autolabel.lean +++ b/scripts/autolabel.lean @@ -293,9 +293,11 @@ unsafe def main (args : List String): IO UInt32 := do -- return 3 -- get the modified files + println "Computing 'git diff --name-only origin/master...HEAD'" let gitDiff ← IO.Process.run { cmd := "git", args := #["diff", "--name-only", "origin/master...HEAD"] } + println s!"---\n{gitDiff}\n---" let modifiedFiles : Array FilePath := (gitDiff.splitOn "\n").toArray.map (⟨·⟩) -- find labels covering the modified files diff --git a/scripts/bench_summary.lean b/scripts/bench_summary.lean index a661a13ad84709..89a03a93d702d2 100644 --- a/scripts/bench_summary.lean +++ b/scripts/bench_summary.lean @@ -54,7 +54,7 @@ def formatPercent (reldiff : Float) : String := let reldiff := reldiff * 10 ^ 4 let sgn : Int := if reldiff < 0 then -1 else 1 let reldiff := (.ofInt sgn) * reldiff - let (sgn, intDigs, decDigs) := intDecs (sgn * reldiff.toUInt32.val) 0 2 + let (sgn, intDigs, decDigs) := intDecs (sgn * reldiff.toUInt32.toFin) 0 2 -- the `if ... then ... else ...` makes sure that the output includes leading `0`s s!"({sgn}{intDigs}.{if decDigs < 10 then "0" else ""}{decDigs}%)" diff --git a/scripts/migrate_to_fork.py b/scripts/migrate_to_fork.py new file mode 100755 index 00000000000000..e550f15aafa04a --- /dev/null +++ b/scripts/migrate_to_fork.py @@ -0,0 +1,772 @@ +#!/usr/bin/env python3 +""" +Script to migrate contributors from direct write access to using forks. + +This script automates the process of migrating mathlib4 contributors from having +direct write access to the main repository to using a fork-based workflow. + +Features: +1. Validates current branch (prevents migration of system branches like master, nightly-testing) +2. Checks GitHub CLI installation and authentication with OS-specific installation instructions +3. Validates GitHub CLI token has required scopes (repo, workflow) with clear error messages +4. Automatically detects SSH connectivity to GitHub and chooses appropriate protocol (SSH preferred, HTTPS fallback) +5. Creates or syncs user's fork of mathlib4 +6. Sets up git remotes correctly (upstream → leanprover-community/mathlib4, origin → user's fork) +7. Detects if migration has already been completed and skips unnecessary steps +8. Migrates current branch to fork with proper upstream tracking +9. Intelligently handles existing PRs: + - Detects open PRs from main repo and offers to migrate them to fork-based PRs + - Detects existing fork-based PRs and confirms no migration needed + - Handles edge cases with both main repo and fork PRs requiring manual cleanup +10. Uses fast delete/re-add approach for remote changes to avoid slow branch tracking updates +11. Respects user's existing remote naming preferences (if user declines to rename remotes, + the script will use whatever remote names point to the correct repositories) + +Usage: + python3 scripts/migrate_to_fork.py # Interactive mode + python3 scripts/migrate_to_fork.py -y # Auto-accept all prompts + +Requirements: + - GitHub CLI (gh) installed and authenticated with required scopes: + gh auth login --scopes 'repo,workflow' + - Git repository must be the mathlib4 repository + - User must be on a feature branch (not master, nightly-testing, or lean-pr-testing-*) + - SSH access to GitHub is recommended but not required (will fallback to HTTPS) + +The script is safe to run multiple times and will skip already-completed migration steps. +""" + +import subprocess +import sys +import json +import re +import argparse +from typing import Optional, Dict, Any, List + + +class Colors: + """ANSI color codes for terminal output.""" + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + BLUE = '\033[94m' + BOLD = '\033[1m' + END = '\033[0m' + + +def print_step(step_num: int, description: str) -> None: + """Print a colored step header.""" + print(f"\n{Colors.BOLD}{Colors.BLUE}=== Step {step_num}: {description} ==={Colors.END}") + + +def print_success(message: str) -> None: + """Print a success message in green.""" + print(f"{Colors.GREEN}✓ {message}{Colors.END}") + + +def print_warning(message: str) -> None: + """Print a warning message in yellow.""" + print(f"{Colors.YELLOW}⚠ {message}{Colors.END}") + + +def print_error(message: str) -> None: + """Print an error message in red.""" + print(f"{Colors.RED}✗ {message}{Colors.END}") + + +def run_command(cmd: List[str], capture_output: bool = True, check: bool = True) -> subprocess.CompletedProcess: + """Run a command and return the result.""" + try: + result = subprocess.run(cmd, capture_output=capture_output, text=True, check=check) + return result + except subprocess.CalledProcessError as e: + if not check: + return e + print_error(f"Command failed: {' '.join(cmd)}") + print_error(f"Error: {e.stderr if e.stderr else str(e)}") + sys.exit(1) + + +def get_user_input(prompt: str, default: Optional[str] = None) -> str: + """Get user input with optional default.""" + if default: + response = input(f"{prompt} [{default}]: ").strip() + return response if response else default + return input(f"{prompt}: ").strip() + + +def yes_no_prompt(prompt: str, default: bool = True, auto_accept: bool = False) -> bool: + """Ask a yes/no question and return the boolean result.""" + if auto_accept: + print(f"{prompt} [Y/n]: Y (auto-accepted)") + return True + + default_str = "Y/n" if default else "y/N" + response = input(f"{prompt} [{default_str}]: ").strip().lower() + + if not response: + return default + return response.startswith('y') + + +def check_gh_installation() -> bool: + """Check if GitHub CLI is installed and authenticated.""" + print_step(1, "Checking GitHub CLI installation and authentication") + + # Check if gh is installed + try: + result = run_command(['gh', '--version'], check=False) + if result.returncode != 0: + print_error("GitHub CLI (gh) returned an error.") + print("Please check your installation or visit: https://cli.github.com/") + return False + print_success("GitHub CLI is installed") + except FileNotFoundError: + print_error("GitHub CLI (gh) is not installed.") + print("Please install it:") + print(" macOS: brew install gh") + print(" Ubuntu/Debian: sudo apt install gh") + print(" Or visit: https://cli.github.com/") + return False + + # Check if authenticated + try: + result = run_command(['gh', 'auth', 'status'], check=False) + if result.returncode != 0: + print_error("GitHub CLI is not authenticated.") + print("Please run: gh auth login") + return False + print_success("GitHub CLI is authenticated") + + # Check token scopes + if not check_gh_token_scopes(): + return False + + return True + except Exception: + print_error("Failed to check GitHub CLI authentication status.") + return False + + +def check_gh_token_scopes() -> bool: + """Check if the GitHub CLI token has required scopes.""" + try: + # Get current token scopes + result = run_command(['gh', 'auth', 'status', '--show-token'], check=False) + if result.returncode != 0: + # Fallback: try to make a test API call that requires repo scope + result = run_command(['gh', 'api', 'user'], check=False) + if result.returncode != 0: + print_error("GitHub CLI token lacks required permissions.") + print("Please re-authenticate with required scopes:") + print(" gh auth login --scopes 'repo,workflow'") + return False + print_warning("Could not verify token scopes, but basic API access works") + return True + + # Parse the output to check for required scopes + auth_output = result.stdout + if 'repo' not in auth_output: # or 'workflow' not in auth_output: + print_error("GitHub CLI token lacks required scopes.") + print("Required scopes: repo, workflow") + print("Please re-authenticate with required scopes:") + print(" gh auth login --scopes 'repo,workflow'") + return False + + print_success("GitHub CLI token has required scopes") + return True + + except Exception as e: + # Try a fallback test - attempt to access repo API + try: + result = run_command(['gh', 'api', 'user/repos', '--limit', '1'], check=False) + if result.returncode != 0: + print_error("GitHub CLI token lacks required 'repo' scope.") + print("Please re-authenticate with required scopes:") + print(" gh auth login --scopes 'repo,workflow'") + return False + print_success("GitHub CLI token appears to have required permissions") + return True + except Exception: + print_warning(f"Could not verify token scopes: {e}") + print("If you encounter permission errors, try re-authenticating:") + print(" gh auth login --scopes 'repo,workflow'") + return True # Continue with warning + + +def check_ssh_github_access() -> bool: + """Check if SSH access to GitHub is available.""" + try: + # Test SSH connection to GitHub + result = run_command(['ssh', '-T', 'git@github.com'], check=False) + # SSH to GitHub returns exit code 1 for successful authentication + # but exit code 255 for connection failures + if result.returncode == 1: + # Check if the output contains successful authentication message + if 'successfully authenticated' in result.stderr: + return True + return False + except Exception: + return False + + +def get_github_username() -> str: + """Get the GitHub username of the current user.""" + print_step(2, "Identifying GitHub username") + + try: + result = run_command(['gh', 'api', 'user']) + user_data = json.loads(result.stdout) + username = user_data['login'] + print_success(f"GitHub username: {username}") + return username + except Exception as e: + print_error(f"Failed to get GitHub username: {e}") + sys.exit(1) + + +def get_remote_url(repo_name: str, use_ssh: bool = True) -> str: + """Get the appropriate remote URL (SSH or HTTPS) for a repository.""" + if use_ssh: + return f"git@github.com:{repo_name}.git" + else: + return f"https://github.com/{repo_name}.git" + + +def check_and_create_fork(username: str, auto_accept: bool = False) -> str: + """Check if user has a fork, create one if needed.""" + print_step(3, "Checking for fork of mathlib4") + + repo_name = f"{username}/mathlib4" + + # Determine if we should use SSH + use_ssh = check_ssh_github_access() + if use_ssh: + print_success("SSH access to GitHub is available - will use SSH URLs") + else: + print_warning("SSH access to GitHub not available - will use HTTPS URLs") + + # Check if fork exists + try: + result = run_command(['gh', 'repo', 'view', repo_name], check=False) + if result.returncode == 0: + print_success(f"Fork exists: {repo_name}") + + # Synchronize master branch + print("Synchronizing fork's master branch...") + try: + run_command(['gh', 'repo', 'sync', repo_name, '--source', 'leanprover-community/mathlib4'], check=False) + print_success("Fork synchronized with upstream") + except Exception as e: + print_warning(f"Failed to sync fork automatically: {e}") + print("You may need to sync manually later") + + return get_remote_url(repo_name, use_ssh) + except Exception: + pass + + # Fork doesn't exist, offer to create it + print(f"Fork {repo_name} does not exist.") + if yes_no_prompt("Would you like to create a fork?", auto_accept=auto_accept): + try: + print("Creating fork...") + run_command(['gh', 'repo', 'fork', 'leanprover-community/mathlib4', '--clone=false']) + print_success(f"Fork created: {repo_name}") + return get_remote_url(repo_name, use_ssh) + except Exception as e: + print_error(f"Failed to create fork: {e}") + sys.exit(1) + else: + print_error("Cannot proceed without a fork") + sys.exit(1) + + +def get_current_remotes() -> Dict[str, str]: + """Get current git remotes.""" + try: + result = run_command(['git', 'remote', '-v']) + remotes = {} + for line in result.stdout.strip().split('\n'): + if line: + parts = line.split() + if len(parts) >= 2 and '(fetch)' in line: + name, url = parts[0], parts[1] + remotes[name] = url + return remotes + except Exception: + return {} + + +def setup_remotes(username: str, fork_url: str, auto_accept: bool = False) -> str: + """Set up upstream and origin remotes correctly. + + Returns the name of the remote that points to the user's fork. + """ + print_step(4, "Setting up git remotes") + + remotes = get_current_remotes() + + # Determine URL format based on SSH availability + use_ssh = check_ssh_github_access() + upstream_url = get_remote_url("leanprover-community/mathlib4", use_ssh) + + # Handle upstream remote + upstream_remote = None + for name, url in remotes.items(): + if 'leanprover-community/mathlib4' in url: + upstream_remote = name + break + + if upstream_remote: + if upstream_remote != 'upstream': + print(f"Found leanprover-community/mathlib4 as remote '{upstream_remote}'") + if yes_no_prompt(f"Rename '{upstream_remote}' to 'upstream'?", auto_accept=auto_accept): + # Show warning about branch tracking + print_warning("⚠️ Changing remote names will reset upstream tracking for all branches.") + print(" This is much faster than renaming, but means other local branches will need") + print(" their upstream reset manually later.") + print(" ✅ Don't worry: This script will automatically fix the upstream for your") + print(" current branch after the remote changes.") + + run_command(['git', 'remote', 'remove', upstream_remote]) + run_command(['git', 'remote', 'add', 'upstream', upstream_url]) + print_success("Replaced remote with 'upstream'") + else: + print_success("Upstream remote already configured correctly") + else: + print("Adding upstream remote...") + run_command(['git', 'remote', 'add', 'upstream', upstream_url]) + print_success("Added upstream remote") + + # Update the remote info after writing to it! + remotes = get_current_remotes() + + # Handle origin remote (fork) + origin_remote = None + for name, url in remotes.items(): + if f'{username}/mathlib4' in url: + origin_remote = name + break + + fork_remote_name = 'origin' # Default expected name + + if origin_remote: + if origin_remote != 'origin': + print(f"Found fork as remote '{origin_remote}'") + if yes_no_prompt(f"Rename '{origin_remote}' to 'origin'?", auto_accept=auto_accept): + # Check if origin exists and is different + if 'origin' in remotes and f'{username}/mathlib4' not in remotes['origin']: + if yes_no_prompt("Remove existing 'origin' remote?", auto_accept=auto_accept): + run_command(['git', 'remote', 'remove', 'origin']) + + # Use delete/re-add approach for speed + run_command(['git', 'remote', 'remove', origin_remote]) + run_command(['git', 'remote', 'add', 'origin', fork_url]) + print_success("Replaced remote with 'origin' pointing to your fork") + fork_remote_name = 'origin' + else: + print_success(f"Keeping fork as remote '{origin_remote}'") + fork_remote_name = origin_remote + else: + print_success("Origin remote (fork) already configured correctly") + fork_remote_name = 'origin' + else: + # Check if origin exists and is not the fork + if 'origin' in remotes: + if f'{username}/mathlib4' not in remotes['origin']: + print(f"Current origin: {remotes['origin']}") + if yes_no_prompt("Replace existing 'origin' with your fork?", auto_accept=auto_accept): + run_command(['git', 'remote', 'remove', 'origin']) + run_command(['git', 'remote', 'add', 'origin', fork_url]) + print_success("Set origin to your fork") + fork_remote_name = 'origin' + else: + run_command(['git', 'remote', 'add', 'fork', fork_url]) + print_warning("Added fork as 'fork' remote instead of 'origin'") + fork_remote_name = 'fork' + else: + print_success("Origin already points to your fork") + fork_remote_name = 'origin' + else: + run_command(['git', 'remote', 'add', 'origin', fork_url]) + print_success("Added origin remote pointing to your fork") + fork_remote_name = 'origin' + + return fork_remote_name + + +def get_current_branch() -> str: + """Get the current git branch name.""" + try: + result = run_command(['git', 'branch', '--show-current']) + return result.stdout.strip() + except Exception: + print_error("Failed to get current branch") + sys.exit(1) + + +def validate_branch_for_migration(branch: str, auto_accept: bool = False) -> None: + """Validate that the current branch can be migrated to a fork.""" + print(f"\n{Colors.BOLD}Current branch: {branch}{Colors.END}") + + # Check for system branches that shouldn't be migrated + invalid_branches = [] + + if branch == 'master': + invalid_branches.append("master (main development branch)") + elif branch == 'nightly-testing': + invalid_branches.append("nightly-testing (CI testing branch)") + elif re.match(r'^lean-pr-testing-\d+$', branch): + invalid_branches.append(f"{branch} (Lean PR testing branch)") + + if invalid_branches: + print_error(f"Cannot migrate branch '{branch}'") + print(f"The branch '{branch}' is a system branch that should not be migrated to a fork.") + print("\nSystem branches that cannot be migrated:") + for invalid_branch in invalid_branches: + print(f" • {invalid_branch}") + + print(f"\n{Colors.BOLD}What you should do:{Colors.END}") + print("1. Switch to the feature branch you want to migrate:") + print(" git checkout your-feature-branch-name") + print("\n2. If you don't have a feature branch yet, create one:") + print(" git checkout -b your-new-feature-branch") + print("\n3. You may need to merge master into your branch to access this script:") + print(" git merge master") + print("\n4. Then run this migration script again") + + sys.exit(1) + + # Additional warning for master branch (in case the check above didn't catch it) + if branch == 'master': + print_warning("You're on the master branch.") + print("It's highly recommended to work on feature branches instead.") + if not yes_no_prompt("Are you sure you want to continue with master?", auto_accept=auto_accept): + print("Please create a feature branch and run the script again.") + sys.exit(0) + + +def check_branch_already_migrated(branch: str, username: str, fork_remote_name: str) -> bool: + """Check if the current branch is already set to push to the user's fork.""" + try: + # Get the upstream branch for the current branch + result = run_command(['git', 'rev-parse', '--abbrev-ref', f'{branch}@{{upstream}}'], check=False) + if result.returncode == 0: + upstream = result.stdout.strip() + # Check if upstream is fork_remote/branch (which should point to the fork) + if upstream.startswith(f'{fork_remote_name}/'): + # Verify that the fork remote actually points to the user's fork + remotes = get_current_remotes() + if fork_remote_name in remotes and f'{username}/mathlib4' in remotes[fork_remote_name]: + print_success(f"Branch '{branch}' is already configured to push to your fork") + return True + return False + except Exception: + return False + + +def push_branch_to_fork(branch: str, fork_remote_name: str) -> None: + """Push current branch to fork and set upstream.""" + print_step(5, f"Pushing branch '{branch}' to fork") + + try: + # Push to fork and set upstream + run_command(['git', 'push', '-u', fork_remote_name, branch]) + print_success(f"Branch '{branch}' pushed to fork and set as upstream") + except Exception as e: + print_error(f"Failed to push branch: {e}") + sys.exit(1) + + +def find_existing_pr(branch: str, username: str) -> Optional[Dict[str, Any]]: + """Check if there's an existing PR for this branch from either main repo or fork.""" + print_step(6, "Checking for existing PR") + + old_style_pr = None + fork_style_pr = None + + try: + # Search for all PRs with this branch name, then filter by head repository + # Include isDraft to preserve draft status during migration + result = run_command(['gh', 'pr', 'list', '--repo', 'leanprover-community/mathlib4', + '--head', f'{branch}', '--json', 'number,title,url,state,headRepositoryOwner,isDraft'], + check=False) + + if result.returncode == 0 and result.stdout.strip(): + prs = json.loads(result.stdout) + for pr in prs: + if pr['state'] == 'OPEN': + # Check if this PR is from the main repo or a fork + head_repo_owner = pr['headRepositoryOwner'].get('login', '') + + if head_repo_owner == 'leanprover-community': + # PR from main repo + old_style_pr = pr + draft_status = " (draft)" if pr.get('isDraft', False) else "" + print_success(f"Found existing open PR from main repo: #{pr['number']} - {pr['title']}{draft_status}") + print(f"URL: {pr['url']}") + elif head_repo_owner == username: + # PR from user's fork + fork_style_pr = pr + draft_status = " (draft)" if pr.get('isDraft', False) else "" + print_success(f"Found existing open PR from your fork: #{pr['number']} - {pr['title']}{draft_status}") + print(f"URL: {pr['url']}") + else: + # Closed PR - just mention it + head_repo_owner = pr['headRepositoryOwner'].get('login', '') + if head_repo_owner == 'leanprover-community': + print(f"Found closed PR from main repo: #{pr['number']} - {pr['title']} (state: {pr['state']})") + elif head_repo_owner == username: + print(f"Found closed PR from your fork: #{pr['number']} - {pr['title']} (state: {pr['state']})") + print("Closed PRs don't need migration.") + + # Return info about what we found (only open PRs) + if fork_style_pr and old_style_pr: + print_warning("Found open PRs from both main repo and fork - this might need manual cleanup") + return { + 'type': 'both', + 'old_pr': old_style_pr, + 'fork_pr': fork_style_pr + } + elif fork_style_pr: + return { + 'type': 'fork', + 'pr': fork_style_pr + } + elif old_style_pr: + return { + 'type': 'old', + 'pr': old_style_pr + } + + return None + + except Exception as e: + print_warning(f"Could not check for existing PR: {e}") + return None + + +def create_new_pr_from_fork(branch: str, username: str, old_pr: Optional[Dict[str, Any]] = None) -> str: + """Create a new PR from the fork.""" + print_step(7, "Creating new PR from fork") + + try: + # Get PR details + if old_pr: + title = old_pr['title'] + is_draft = old_pr.get('isDraft', False) + print(f"Using title from old PR: {title}") + if is_draft: + print_success("Original PR is a draft - new PR will also be created as draft") + + # Fetch full PR details including body and labels + print("Fetching complete PR details...") + try: + result = run_command(['gh', 'pr', 'view', str(old_pr['number']), + '--repo', 'leanprover-community/mathlib4', + '--json', 'body,labels']) + pr_details = json.loads(result.stdout) + + original_body = pr_details.get('body', '') or '' + labels = pr_details.get('labels', []) + + # Prepare the new body with migration notice + if original_body.strip(): + body = f"{original_body}\n\n---\n\n*This PR continues the work from #{old_pr['number']}.*\n\n*Original PR: {old_pr['url']}*" + else: + body = f"*This PR continues the work from #{old_pr['number']}.*\n\n*Original PR: {old_pr['url']}*" + + print_success(f"Found {len(labels)} labels to copy: {', '.join([label['name'] for label in labels])}" if labels else "No labels found on original PR") + + except Exception as e: + print_warning(f"Could not fetch full PR details: {e}") + # Fallback to simple body + body = f"This PR continues the work from #{old_pr['number']}.\n\nOriginal PR: {old_pr['url']}" + labels = [] + else: + title = get_user_input("Enter PR title") + body = '' + labels = [] + is_draft = False + + # Create PR from fork + cmd = ['gh', 'pr', 'create', + '--repo', 'leanprover-community/mathlib4', + '--head', f'{username}:{branch}', + '--title', title, + '--body', body] + + # Add draft flag if the original PR was a draft + if old_pr and is_draft: + cmd.append('--draft') + + result = run_command(cmd) + + # Extract PR URL and number from output + pr_url = result.stdout.strip() + print_success(f"New PR created: {pr_url}") + + # Extract PR number from URL for label operations + pr_number_match = re.search(r'/pull/(\d+)', pr_url) + if not pr_number_match: + print_warning("Could not extract PR number from URL - skipping label operations") + return pr_url + + new_pr_number = pr_number_match.group(1) + + # Copy labels if we have any + if labels and old_pr: + print("Copying labels to new PR...") + label_names = [label['name'] for label in labels] + + try: + # Try to add all labels at once + for label_name in label_names: + run_command(['gh', 'pr', 'edit', new_pr_number, + '--repo', 'leanprover-community/mathlib4', + '--add-label', label_name]) + print_success(f"Successfully copied {len(label_names)} labels") + + except Exception as e: + print_warning(f"Failed to add labels directly: {e}") + print("Falling back to adding labels as comments...") + + # Fallback: add each label as a comment + for label_name in label_names: + try: + run_command(['gh', 'pr', 'comment', new_pr_number, + '--repo', 'leanprover-community/mathlib4', + '--body', label_name]) + except Exception as comment_error: + print_warning(f"Failed to add comment for label '{label_name}': {comment_error}") + + print_success(f"Added {len(label_names)} label comments as fallback") + + return pr_url + + except Exception as e: + print_error(f"Failed to create new PR: {e}") + sys.exit(1) + + +def close_old_pr_and_comment(old_pr: Dict[str, Any], new_pr_url: str) -> None: + """Close the old PR and add a comment linking to the new one.""" + print_step(8, "Closing old PR and adding comment") + + try: + # Add comment to old PR + comment = f"This PR has been migrated to a fork-based workflow: {new_pr_url}" + run_command(['gh', 'pr', 'comment', str(old_pr['number']), + '--repo', 'leanprover-community/mathlib4', + '--body', comment]) + print_success("Added comment to old PR") + + # Close old PR + run_command(['gh', 'pr', 'close', str(old_pr['number']), + '--repo', 'leanprover-community/mathlib4']) + print_success(f"Closed old PR #{old_pr['number']}") + + except Exception as e: + print_error(f"Failed to close old PR: {e}") + + +def main() -> None: + """Main migration workflow.""" + # Parse command line arguments + parser = argparse.ArgumentParser(description="Script to migrate contributors from direct write access to using forks.") + parser.add_argument('-y', '--auto-accept', action='store_true', help="Auto-accept all prompts") + args = parser.parse_args() + + print(f"{Colors.BOLD}🔄 Mathlib4 Fork Migration Script{Colors.END}") + print("This script will help you migrate from direct write access to using forks.\n") + + if args.auto_accept: + print(f"{Colors.YELLOW}ℹ️ Auto-accept mode enabled - all prompts will be automatically accepted{Colors.END}\n") + + # Step 1: Check gh installation + if not check_gh_installation(): + sys.exit(1) + + # Step 2: Get username + username = get_github_username() + + # Step 3: Check/create fork + fork_url = check_and_create_fork(username, args.auto_accept) + + # Step 4: Setup remotes + fork_remote_name = setup_remotes(username, fork_url, args.auto_accept) + + # Get current branch and validate + current_branch = get_current_branch() + validate_branch_for_migration(current_branch, args.auto_accept) + + # Check if branch is already migrated + branch_already_migrated = check_branch_already_migrated(current_branch, username, fork_remote_name) + + # Step 5: Push branch to fork (if needed) + if not branch_already_migrated: + push_branch_to_fork(current_branch, fork_remote_name) + else: + print(f"✅ Branch '{current_branch}' is already configured to push to your fork - skipping migration") + + # Step 6: Check for existing PRs + existing_pr_info = find_existing_pr(current_branch, username) + + # Step 7 & 8: Handle PR migration based on what we found + if existing_pr_info: + pr_type = existing_pr_info['type'] + + if pr_type == 'fork': + # PR already exists from fork - nothing to do + pr = existing_pr_info['pr'] + print_success(f"✅ PR #{pr['number']} already exists from your fork - no migration needed") + print(f"URL: {pr['url']}") + + elif pr_type == 'old': + # Old-style PR exists, offer to migrate it + old_pr = existing_pr_info['pr'] + if yes_no_prompt("Create new PR from fork and close the old one?", auto_accept=args.auto_accept): + new_pr_url = create_new_pr_from_fork(current_branch, username, old_pr) + close_old_pr_and_comment(old_pr, new_pr_url) + else: + print("Skipping PR migration. You can create a new PR manually if needed.") + + elif pr_type == 'both': + # Both old and new style PRs exist - manual cleanup needed + old_pr = existing_pr_info['old_pr'] + fork_pr = existing_pr_info['fork_pr'] + print_warning("⚠️ Found PRs from both main repo and fork:") + print(f" Main repo PR: #{old_pr['number']} - {old_pr['title']}") + print(f" Fork PR: #{fork_pr['number']} - {fork_pr['title']}") + print("\nThis situation requires manual cleanup. You may want to:") + print("1. Review both PRs to see which one is current") + print("2. Close the outdated one manually") + print("3. Update the active PR if needed") + else: + # No existing PR found - just note it, don't offer to create one + print("✓ No existing PR found for this branch") + + print(f"\n{Colors.BOLD}{Colors.GREEN}🎉 Migration completed successfully!{Colors.END}") + + # Show summary of current state + print(f"\n{Colors.BOLD}Current state:{Colors.END}") + print(f"✓ Branch '{current_branch}' is configured to push to your fork") + print(f"✓ Git remotes are set up correctly (fork remote: '{fork_remote_name}')") + if existing_pr_info and existing_pr_info['type'] in ['fork', 'both']: + pr_info = existing_pr_info.get('pr') or existing_pr_info.get('fork_pr') + print(f"✓ PR #{pr_info['number']} exists from your fork") + + print(f"\n{Colors.BOLD}Next steps:{Colors.END}") + print(f"1. Future pushes will automatically go to your fork ('{fork_remote_name}' remote)") + print("2. Create PRs from your fork to leanprover-community/mathlib4") + print("3. Remember to sync your fork regularly with upstream:") + print(" gh repo sync --source leanprover-community/mathlib4") + print("4. To update other branches, checkout and run this script again") + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print(f"\n{Colors.YELLOW}Migration cancelled by user{Colors.END}") + sys.exit(0) diff --git a/scripts/noshake.json b/scripts/noshake.json index 956a1610e7303b..c515b4bf6477f4 100644 --- a/scripts/noshake.json +++ b/scripts/noshake.json @@ -102,7 +102,7 @@ "Mathlib.Tactic.Find", "Mathlib.Tactic.Finiteness", "Mathlib.Tactic.GCongr", - "Mathlib.Tactic.GCongr.CoreAttrs", + "Mathlib.Tactic.GRewrite", "Mathlib.Tactic.Generalize", "Mathlib.Tactic.GeneralizeProofs", "Mathlib.Tactic.Group", @@ -212,6 +212,8 @@ "Mathlib.Topology.Defs.Basic": ["Mathlib.Tactic.FunProp"], "Mathlib.Topology.Category.UniformSpace": ["Mathlib.CategoryTheory.Monad.Limits"], + "Mathlib.Topology.Algebra.RestrictedProduct.Basic": + ["Mathlib.Order.Filter.Cofinite"], "Mathlib.Topology.Algebra.Nonarchimedean.AdicTopology": ["Mathlib.Topology.Algebra.UniformRing"], "Mathlib.Tactic.Widget.StringDiagram": @@ -266,8 +268,8 @@ ["Mathlib.Data.Subtype", "Mathlib.Util.TermReduce"], "Mathlib.Tactic.ITauto": ["Batteries.Tactic.Init", "Mathlib.Logic.Basic"], "Mathlib.Tactic.Group": ["Mathlib.Algebra.Group.Commutator"], - "Mathlib.Tactic.GCongr.Core": - ["Mathlib.Order.Defs", "Mathlib.Order.Defs.PartialOrder"], + "Mathlib.Tactic.GCongr.CoreAttrs": ["Mathlib.Tactic.GCongr.Core"], + "Mathlib.Tactic.GCongr.Core": ["Mathlib.Order.Defs"], "Mathlib.Tactic.GCongr": ["Mathlib.Tactic.Positivity.Core"], "Mathlib.Tactic.FunProp.Mor": ["Mathlib.Data.FunLike.Basic"], "Mathlib.Tactic.FunProp.Differentiable": @@ -395,7 +397,6 @@ ["Mathlib.LinearAlgebra.AffineSpace.Defs"], "Mathlib.LinearAlgebra.AffineSpace.AffineMap": ["Mathlib.LinearAlgebra.AffineSpace.Defs"], - "Mathlib.Lean.Meta.RefinedDiscrTree.Pi": ["Mathlib.Algebra.Notation.Pi"], "Mathlib.Lean.Meta": ["Batteries.Logic"], "Mathlib.Lean.Expr.ExtraRecognizers": ["Mathlib.Data.Set.Operations"], "Mathlib.Lean.Expr.Basic": ["Batteries.Logic"], @@ -422,6 +423,7 @@ ["Batteries.Tactic.Init", "Mathlib.Logic.Function.Defs"], "Mathlib.Data.Nat.Init": ["Batteries.Tactic.Init"], "Mathlib.Data.Nat.Find": ["Batteries.WF"], + "Mathlib.Data.Nat.Basic": ["Mathlib.Tactic.GCongr.Core"], "Mathlib.Data.Multiset.Bind": ["Mathlib.Algebra.GroupWithZero.Action.Defs"], "Mathlib.Data.Matroid.Rank.ENat": ["Mathlib.Tactic.TautoSet"], "Mathlib.Data.Matroid.Minor.Contract": ["Mathlib.Tactic.TautoSet"], @@ -525,6 +527,7 @@ ["Mathlib.Algebra.Group.Pointwise.Finset.Basic"], "Mathlib.Algebra.Group.Pi.Basic": ["Batteries.Tactic.Classical"], "Mathlib.Algebra.Group.Idempotent": ["Mathlib.Data.Subtype"], + "Mathlib.Algebra.Group.Even": ["Mathlib.Data.Set.Operations"], "Mathlib.Algebra.Group.Defs": ["Mathlib.Algebra.Notation.Defs", "Mathlib.Tactic.OfNat"], "Mathlib.Algebra.GeomSum": ["Mathlib.Algebra.Order.BigOperators.Ring.Finset"], diff --git a/scripts/user_activity_report.py b/scripts/user_activity_report.py new file mode 100755 index 00000000000000..24ae9ec1087249 --- /dev/null +++ b/scripts/user_activity_report.py @@ -0,0 +1,512 @@ +#!/usr/bin/env python3 +""" +Generate a report of repository users and their last commit activity. + +This script finds all users with access to the repository, determines when they +last made a commit, and generates a table showing username, age of last commit, +and access level, sorted by age of last commit (most recent first). + +Features: +- Fetches repository collaborators and organization members +- Caches user lists (24h expiry) and commit data (6h expiry) for performance +- Supports filtering by access level (--admin, --write) +- Supports single user analysis (--user USERNAME) +- Supports limiting results for debugging (--limit N) +- Can generate removal commands for inactive users (--remove N) +- Uses gh CLI for authenticated GitHub API access + +The --remove flag generates (but does not execute) gh commands to remove +write access from non-admin users who haven't committed in more than N days. +Bot accounts (usernames ending with '-bot') are automatically excluded from +removal commands. + +Results are cached to avoid repeated API calls. Cache files are automatically +saved after each new commit lookup to prevent data loss. + +Requires 'gh' (GitHub CLI) to be installed and authenticated. +""" + +import argparse +import json +import os +import subprocess +import sys +from datetime import datetime, timezone, timedelta +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +# Get cache directory following XDG Base Directory Specification +cache_home = os.environ.get('XDG_CACHE_HOME') +if cache_home: + CACHE_DIR = Path(cache_home) / "user_activity_report" +else: + CACHE_DIR = Path.home() / ".cache" / "user_activity_report" + + +def get_cache_files(owner: str, repo: str) -> Tuple[Path, Path]: + """Get repository-specific cache file paths.""" + # Ensure cache directory exists + CACHE_DIR.mkdir(parents=True, exist_ok=True) + + # Replace slash with underscore to make it filesystem-safe + repo_slug = f"{owner}_{repo}" + users_cache_file = CACHE_DIR / f"users_cache_{repo_slug}.json" + commits_cache_file = CACHE_DIR / f"commits_cache_{repo_slug}.json" + return users_cache_file, commits_cache_file + + +def check_gh_auth() -> bool: + """Check if gh is installed and authenticated.""" + try: + result = subprocess.run(['gh', 'auth', 'status'], capture_output=True, text=True, check=False) + return result.returncode == 0 + except FileNotFoundError: + print("Error: 'gh' command not found. Please install GitHub CLI.", file=sys.stderr) + return False + + +def get_repo_info(repo_arg: Optional[str] = None) -> Tuple[str, str]: + """Get repository owner and name from command line argument or git remote.""" + if repo_arg: + try: + if '/' not in repo_arg: + raise ValueError("Repository must be in format 'owner/repo'") + owner, repo = repo_arg.split('/', 1) + if not owner or not repo: + raise ValueError("Repository must be in format 'owner/repo'") + return owner, repo + except ValueError as e: + print(f"Error: Invalid repository format '{repo_arg}'. {e}", file=sys.stderr) + sys.exit(1) + + # Fall back to git remote detection + try: + result = subprocess.run( + ['git', 'remote', 'get-url', 'origin'], + capture_output=True, text=True, check=True + ) + url = result.stdout.strip() + + if url.startswith('git@github.com:'): + # SSH format: git@github.com:owner/repo.git + repo_path = url.replace('git@github.com:', '').replace('.git', '') + elif url.startswith('https://github.com/'): + # HTTPS format: https://github.com/owner/repo.git + repo_path = url.replace('https://github.com/', '').replace('.git', '') + else: + raise ValueError(f"Unknown git remote format: {url}") + + owner, repo = repo_path.split('/') + + # If this is a fork of mathlib, use the upstream repository instead + if repo.lower() == 'mathlib4' and owner != 'leanprover-community': + print(f"Detected fork of mathlib ({owner}/{repo}), using upstream leanprover-community/mathlib4 instead") + return 'leanprover-community', 'mathlib4' + + return owner, repo + except (subprocess.CalledProcessError, ValueError, IndexError) as e: + print(f"Error getting repository info: {e}", file=sys.stderr) + print("Tip: Use --repo owner/repo to specify a repository manually", file=sys.stderr) + sys.exit(1) + + +def get_repo_collaborators(owner: str, repo: str, limit: Optional[int] = None) -> List[Dict]: + """Get repository collaborators using gh CLI.""" + print("Fetching repository collaborators...") + + collaborators = [] + + # Get collaborators + try: + cmd = ['gh', 'api', f'repos/{owner}/{repo}/collaborators', '--paginate'] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + # The --paginate flag returns all pages concatenated as a single JSON array + collaborators_data = json.loads(result.stdout) + + for collaborator in collaborators_data: + collaborators.append(collaborator) + if limit and len(collaborators) >= limit: + break + + except subprocess.CalledProcessError as e: + print(f"Warning: Could not fetch collaborators: {e}") + except json.JSONDecodeError as e: + print(f"Warning: Could not parse collaborators response: {e}") + + # Also try to get organization members if this is an org repo + try: + cmd = ['gh', 'api', f'orgs/{owner}/members', '--paginate'] + result = subprocess.run(cmd, capture_output=True, text=True, check=False) + if result.returncode == 0: + org_members = json.loads(result.stdout) + # Add org members who aren't already in collaborators list + existing_logins = {c['login'] for c in collaborators} + for member in org_members: + if member['login'] not in existing_logins: + # Add with 'member' permission level + member['permissions'] = {'admin': False, 'maintain': False, 'push': False, 'triage': False, 'pull': True} + member['role_name'] = 'member' + collaborators.append(member) + if limit and len(collaborators) >= limit: + break + except (subprocess.CalledProcessError, json.JSONDecodeError): + # Org API might not be accessible, that's okay + pass + + return collaborators + + +def get_contributors_fallback(owner: str, repo: str, limit: Optional[int] = None) -> List[Dict]: + """Get repository contributors as fallback using gh CLI.""" + print("Fetching repository contributors (fallback)...") + + contributors = [] + + try: + cmd = ['gh', 'api', f'repos/{owner}/{repo}/contributors', '--paginate'] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + # The --paginate flag returns all pages concatenated as a single JSON array + contributors_data = json.loads(result.stdout) + + for contributor in contributors_data: + # Add with 'contributor' permission level + contributor['permissions'] = {'admin': False, 'maintain': False, 'push': False, 'triage': False, 'pull': True} + contributor['role_name'] = 'contributor' + contributors.append(contributor) + if limit and len(contributors) >= limit: + break + + except subprocess.CalledProcessError as e: + print(f"Warning: Could not fetch contributors: {e}") + except json.JSONDecodeError as e: + print(f"Warning: Could not parse contributors response: {e}") + + return contributors + + +def load_users_cache(owner: str, repo: str) -> Optional[List[Dict]]: + """Load cached users data.""" + users_cache_file, commits_cache_file = get_cache_files(owner, repo) + if users_cache_file.exists(): + try: + with open(users_cache_file, 'r') as f: + data = json.load(f) + # Check if cache is less than 24 hours old + cache_time = datetime.fromisoformat(data['timestamp']) + if (datetime.now(timezone.utc) - cache_time).total_seconds() < 24 * 3600: + print("Using cached user data...") + return data['users'] + except (json.JSONDecodeError, KeyError, ValueError): + pass + return None + + +def save_users_cache(owner: str, repo: str, users: List[Dict]) -> None: + """Save users data to cache.""" + users_cache_file, commits_cache_file = get_cache_files(owner, repo) + cache_data = { + 'timestamp': datetime.now(timezone.utc).isoformat(), + 'users': users + } + with open(users_cache_file, 'w') as f: + json.dump(cache_data, f, indent=2) + + +def load_commits_cache(owner: str, repo: str) -> Dict[str, str]: + """Load cached commit data.""" + users_cache_file, commits_cache_file = get_cache_files(owner, repo) + if commits_cache_file.exists(): + try: + with open(commits_cache_file, 'r') as f: + data = json.load(f) + # Check if cache is less than 6 hours old + cache_time = datetime.fromisoformat(data['timestamp']) + if (datetime.now(timezone.utc) - cache_time).total_seconds() < 6 * 3600: + print("Using cached commit data...") + return data['commits'] + except (json.JSONDecodeError, KeyError, ValueError): + pass + return {} + + +def save_commits_cache(owner: str, repo: str, commits: Dict[str, str]) -> None: + """Save commit data to cache.""" + users_cache_file, commits_cache_file = get_cache_files(owner, repo) + cache_data = { + 'timestamp': datetime.now(timezone.utc).isoformat(), + 'commits': commits + } + with open(commits_cache_file, 'w') as f: + json.dump(cache_data, f, indent=2) + + +def get_last_commit_for_user(owner: str, repo: str, username: str) -> Optional[str]: + """Get the date of the last commit by a user using gh CLI.""" + try: + # Use query string format for parameters + cmd = ['gh', 'api', f'repos/{owner}/{repo}/commits?author={username}&per_page=1'] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + commits = json.loads(result.stdout) + if commits: + commit_date = commits[0]['commit']['author']['date'] + return commit_date + except subprocess.CalledProcessError as e: + print(f"Warning: Could not fetch commits for {username}: {e}") + except json.JSONDecodeError as e: + print(f"Warning: Could not parse commits response for {username}: {e}") + + return None + + +def format_time_ago(date_str: str) -> str: + """Format a datetime string as 'X days ago' or similar.""" + if not date_str: + return "Never" + + try: + commit_date = datetime.fromisoformat(date_str.replace('Z', '+00:00')) + now = datetime.now(timezone.utc) + diff = now - commit_date + + days = diff.days + if days == 0: + hours = diff.seconds // 3600 + if hours == 0: + minutes = diff.seconds // 60 + return f"{minutes} minute{'s' if minutes != 1 else ''} ago" + return f"{hours} hour{'s' if hours != 1 else ''} ago" + elif days == 1: + return "1 day ago" + elif days < 30: + return f"{days} days ago" + elif days < 365: + months = days // 30 + return f"{months} month{'s' if months != 1 else ''} ago" + else: + years = days // 365 + return f"{years} year{'s' if years != 1 else ''} ago" + except ValueError: + return "Invalid date" + + +def get_permission_level(user_data: Dict) -> str: + """Extract permission level from user data.""" + if 'role_name' in user_data: + return user_data['role_name'] + + permissions = user_data.get('permissions', {}) + if permissions.get('admin'): + return 'admin' + elif permissions.get('maintain'): + return 'maintain' + elif permissions.get('push'): + return 'write' + elif permissions.get('triage'): + return 'triage' + elif permissions.get('pull'): + return 'read' + else: + return 'unknown' + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--limit', type=int, help='Limit the number of users to process (for debugging)') + parser.add_argument('--user', type=str, help='Only show results for a specific user (for debugging)') + parser.add_argument('--no-cache', action='store_true', help='Skip cache and fetch fresh data') + parser.add_argument('--contributors-only', action='store_true', help='Use contributors API instead of collaborators (works without special permissions)') + parser.add_argument('--write', action='store_true', help='Only show users with write access or higher') + parser.add_argument('--admin', action='store_true', help='Only show users with admin access') + parser.add_argument('--remove', type=int, metavar='N', help='Print gh commands to remove write access from non-admin users inactive for more than N days (does not execute)') + parser.add_argument('--repo', type=str, help='Specify a repository in the format "owner/repo" (defaults to current git repository)') + + args = parser.parse_args() + + # Check gh authentication + if not check_gh_auth(): + print("Error: 'gh' is not authenticated. Please run 'gh auth login' first.", file=sys.stderr) + sys.exit(1) + + # Get repository info + owner, repo = get_repo_info(args.repo) + print(f"Analyzing repository: {owner}/{repo}") + + # Get users with access + users = None if args.no_cache else load_users_cache(owner, repo) + if users is None: + if args.contributors_only: + users = get_contributors_fallback(owner, repo, args.limit) + else: + users = get_repo_collaborators(owner, repo, args.limit) + # If no collaborators found (e.g., no access), fall back to contributors + if not users: + print("No collaborators found, falling back to contributors...") + users = get_contributors_fallback(owner, repo, args.limit) + save_users_cache(owner, repo, users) + + # Apply limit to cached data as well + if args.limit and len(users) > args.limit: + users = users[:args.limit] + + # Apply access level filters + if args.admin or args.write: + filtered_users = [] + for user in users: + permission_level = get_permission_level(user) + if args.admin and permission_level == 'admin': + filtered_users.append(user) + elif args.write and permission_level in ['write', 'admin', 'maintain']: + filtered_users.append(user) + users = filtered_users + if not users: + print("No users found matching the specified access level criteria.") + sys.exit(1) + + if args.user: + users = [user for user in users if user['login'] == args.user] + if not users: + print(f"User '{args.user}' not found in repository collaborators/contributors.") + # Try to get commit info for this user anyway + users = [{ + 'login': args.user, + 'html_url': f'https://github.com/{args.user}', + 'permissions': {}, + 'role_name': 'unknown' + }] + + if not users: + print("No users found to analyze.") + sys.exit(1) + + print(f"Processing {len(users)} users...") + + # Get cached commit data + cached_commits = {} if args.no_cache else load_commits_cache(owner, repo) + + # Get last commit for each user + user_commits = [] + + for i, user in enumerate(users, 1): + username = user['login'] + + # Check cache first - distinguish between "not cached" vs "cached as None" + if username in cached_commits: + last_commit_date = cached_commits[username] + print(f"Using cached data for {username} ({i}/{len(users)})...") + else: + print(f"Fetching commits for {username} ({i}/{len(users)})...") + last_commit_date = get_last_commit_for_user(owner, repo, username) + cached_commits[username] = last_commit_date + # Save cache after each new lookup + save_commits_cache(owner, repo, cached_commits) + + permission_level = get_permission_level(user) + + user_commits.append({ + 'username': username, + 'last_commit_date': last_commit_date, + 'permission_level': permission_level, + 'profile_url': user['html_url'] + }) + + # Sort by last commit date (most recent first, then never committed) + def sort_key(user_data): + if user_data['last_commit_date'] is None: + # Never committed users should be treated as the oldest (very early date) + return datetime.min.replace(tzinfo=timezone.utc) + try: + # Parse the ISO date string + commit_date = datetime.fromisoformat(user_data['last_commit_date'].replace('Z', '+00:00')) + return commit_date + except ValueError: + # If date parsing fails, treat as very old + return datetime.min.replace(tzinfo=timezone.utc) + + user_commits.sort(key=sort_key, reverse=True) + + # Handle --remove flag + if args.remove is not None: + print(f"\n{'='*80}") + print(f"COMMANDS TO REMOVE INACTIVE NON-ADMIN USERS (>{args.remove} days)") + print(f"{'='*80}") + print("# The following commands would remove write access from non-admin users") + print("# who haven't committed in more than {} days:".format(args.remove)) + print("# (Bot accounts ending with '-bot' are excluded)") + print() + + now = datetime.now(timezone.utc) + cutoff_date = now - timedelta(days=args.remove) + removal_commands = [] + + for user_data in user_commits: + username = user_data['username'] + permission_level = user_data['permission_level'] + last_commit_date = user_data['last_commit_date'] + + # Skip bot accounts + if username.endswith('-bot'): + continue + + # Only consider non-admin users with write access + if permission_level in ['write', 'maintain'] and permission_level != 'admin': + should_remove = False + + if last_commit_date is None: + # Never committed - should be removed + should_remove = True + reason = "never committed" + else: + try: + commit_date = datetime.fromisoformat(last_commit_date.replace('Z', '+00:00')) + if commit_date < cutoff_date: + days_ago = (now - commit_date).days + should_remove = True + reason = f"last commit {days_ago} days ago" + except ValueError: + # If we can't parse the date, treat as old + should_remove = True + reason = "unparseable commit date" + + if should_remove: + cmd = f"gh api repos/{owner}/{repo}/collaborators/{username} -X DELETE" + removal_commands.append((cmd, username, reason)) + + if removal_commands: + for cmd, username, reason in removal_commands: + print(cmd) + print(f"\n# Total users to remove: {len(removal_commands)}") + else: + print("# No users found matching removal criteria") + + print(f"\n# NOTE: These commands are NOT executed automatically.") + print(f"# Review carefully before running any of these commands.") + return + + # Generate report + print("\n" + "="*80) + print("REPOSITORY USER ACTIVITY REPORT") + print("="*80) + print(f"Repository: {owner}/{repo}") + print(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print(f"Total users analyzed: {len(user_commits)}") + print() + + # Table header + print(f"{'Username':<25} {'Last Commit':<20} {'Access Level':<15} {'Profile'}") + print("-" * 80) + + for user_data in user_commits: + username = user_data['username'] + last_commit = format_time_ago(user_data['last_commit_date']) + permission = user_data['permission_level'] + profile_url = user_data['profile_url'] + + print(f"{username:<25} {last_commit:<20} {permission:<15} {profile_url}") + + print() + print(f"Note: Cached data is used when less than 24 hours old (users) or 6 hours old (commits).") + print(f"Use --no-cache to force fresh data retrieval.") + + +if __name__ == '__main__': + main()